简体   繁体   中英

Lifetime inference problem when implementing iterator with refs

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.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM