Проблема логического вывода при реализации итератора с ссылками - PullRequest
0 голосов
/ 02 сентября 2018

Я реализую простой итератор для структуры, содержащей ссылку:

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));
}

Но я получаю следующую ошибку:

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`.

У меня вопрос, как сказать компилятору Rust использовать здесь соответствующее время жизни? Несмотря на то, что я определил XlsxSheet <'a> с помощью модификатора времени жизни и хочу связать имя с &' a str, но почему-то это не приводит к действительному коду Rust.

1 Ответ

0 голосов
/ 02 сентября 2018

Простое решение : Эту проблему можно легко устранить, используя String вместо &'a str.

Объяснение

Я не знаю определения by_index, которое, по-видимому, имеет решающее значение для этой проблемы. Следующее рассуждение является чистой догадкой и не является надежным. Предлагается только для справки.

  1. self.archive заимствует self (который действителен для всей области, скажем, время жизни называется 'me) и имеет время жизни 'me.
  2. Таким образом, возвращаемое значение by_index имеет время жизни 'me.
  3. Упс, XlsxSheet<'me> несовместимо с XlsxSheet<'a> (что ожидается)!

Здесь нам нужно, чтобы XlsxSheet<'me> был подтипом XlsxSheet<'a>, что, в свою очередь, означает, что 'me является подтипом 'a, если XlsxSheet является ковариантным. Поэтому вы можете указать их явно

fn next(&mut self) -> Option<XlsxSheet<'a>> where Self: 'a
// or
impl<'a> Iterator for XlsxFile<'a> + 'a
...