1

I'm implementing a simple Iterator for a struct that contains a ref:

extern crate zip;
extern crate quick_xml;
extern crate chrono;

use std::io::{Seek, Write, Read, Error};
use std::fs::File;
use xlsx_read::zip::read::ZipFile;
use xlsx_read::zip::result::ZipResult;
use xlsx_read::zip::ZipArchive;
use xlsx_read::zip::write::{FileOptions, ZipWriter};
use xlsx_read::quick_xml::Reader as XmlReader;
use xlsx_read::quick_xml::events::Event;
use std::io::BufReader;
use xlsx_read::chrono::prelude::*;

pub struct XlsxFile<'a> {
    path: &'a str,
    archive: ZipArchive<File>,
    sheet_count: usize,
    curr: usize,
}

impl<'a> XlsxFile<'a> {
    pub fn from(path: &'a str) -> Result<XlsxFile, Error> {
        let file = File::open(path)?;
        let archive = ZipArchive::new(file)?;
        let sheet_count = archive.len();

        Ok(XlsxFile {
            path: path,
            archive: archive,
            sheet_count,
            curr: 0,
        })
    }
}

pub struct XlsxSheet<'a> {
    pub name: &'a str,
    pub index: usize,
}

impl<'a> Iterator for XlsxFile<'a> {
    type Item = XlsxSheet<'a>;

    fn next(&mut self) -> Option<XlsxSheet<'a>> {
        loop {
            if self.sheet_count > 0 &&
                self.sheet_count > self.curr {
                let zip_file = self.archive.by_index(self.curr).unwrap();
                let file_name = zip_file.name();
                if file_name.contains("xl/worksheets/sheet") {
                    let sheet = XlsxSheet {
                        name: file_name, // works fine if String::from(file_name) is used
                        index: self.curr,
                    };
                    self.curr += 1;
                    return Some(sheet);
                }
                self.curr += 1;
                continue;
            } else {
                break;
            }
        }
        return None;
    }
}

static XLSX_FILE: &'static str = "<location_to_xlsx_file>";

fn main() {
    let mut file = xlsx_read::XlsxFile::from(XLSX_FILE).unwrap();

    file.for_each(|s| println!("idx: {:?}", s.name));
}

But I get the following error:

error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
  --> src/xlsx_read.rs:50:45
   |
50 |                 let zip_file = self.archive.by_index(self.curr).unwrap();
   |                                             ^^^^^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 46:5...
  --> src/xlsx_read.rs:46:5
   |
46 | /     fn next(&mut self) -> Option<XlsxSheet<'a>> {
47 | |         loop {
48 | |             if self.sheet_count > 0 &&
49 | |                 self.sheet_count > self.curr {
...  |
66 | |         return None;
67 | |     }
   | |_____^
note: ...so that reference does not outlive borrowed content
  --> src/xlsx_read.rs:50:32
   |
50 |                 let zip_file = self.archive.by_index(self.curr).unwrap();
   |                                ^^^^^^^^^^^^
note: but, the lifetime must be valid for the lifetime 'a as defined on the impl at 43:1...
  --> src/xlsx_read.rs:43:1
   |
43 | impl<'a> Iterator for XlsxFile<'a> {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   = note: ...so that the expression is assignable:
           expected std::option::Option<xlsx_read::XlsxSheet<'a>>
              found std::option::Option<xlsx_read::XlsxSheet<'_>>

error: aborting due to previous error

For more information about this error, try `rustc --explain E0495`.

My question is, how to tell Rust compiler to use appropriate lifetime here? Even though I've defined XlsxSheet<'a> with lifetime modifier and want to tie name to &'a str but somehow this doesn't translate into a valid Rust code.

2
  • On the Rust tag it is usual to include all necessary use in the code example, and when appropriate, a link to the playground as well. This helps people that want to answer the question to help you! Right now I don't have time to find those missing use and make some tests, but looking quickly, it looks like you are missing a lifetime on self itself: fn next(&'a mut self) -> Option<XlsxSheet<'a>> Commented Sep 2, 2018 at 0:34
  • @mcarton I've added import statements and a main method. I also tried to use fn next(&'a mut self) but then it started giving me different error msg. error[E0308]: method not compatible with trait --> src/xlsx_read.rs:46:5 | 46 | fn next(&'a mut self) -> Option<XlsxSheet<'a>> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetime mismatch | = note: expected type fn(&mut xlsx_read::XlsxFile<'a>) -> std::option::Option<xlsx_read::XlsxSheet<'_>> found type fn(&'a mut xlsx_read::XlsxFile<'a>) -> std::option::Option<xlsx_read::XlsxSheet<'a>> Commented Sep 2, 2018 at 8:41

1 Answer 1

0

Easy Solution: This problem can be trivially fixed by using String instead of &'a str.

Explanation:

I don't know the definition of by_index, which seems to be quite crucial to this problem. The following reasoning is pure guess and it's not reliable. It's offered only for reference.

  1. self.archive borrows self (which is valid over the entire scope, let's say the lifetime is named 'me), and has lifetime 'me.
  2. Thus the return value of by_index has lifetime 'me.
  3. Oops, XlsxSheet<'me> is not compatible with XlsxSheet<'a> (which is expected)!

What we want here is XlsxSheet<'me> being a subtype of XlsxSheet<'a>, which in turn implies 'me being a subtype of 'a, if XlsxSheet is covariant. Therefore, you can state them explicitly

fn next(&mut self) -> Option<XlsxSheet<'a>> where Self: 'a
// or
impl<'a> Iterator for XlsxFile<'a> + 'a
Sign up to request clarification or add additional context in comments.

1 Comment

I've already commented the line where the easy solution can be used (// works fine if String::from(file_name) is used). I'm actually looking to explore lifetimes in Rust much deeper with this problem. The other approaches don't compile successfully.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.