Проблема времени жизни итератора при возврате ссылок на внутреннюю коллекцию - PullRequest
0 голосов
/ 21 марта 2020

У меня есть структура, которая лениво загружает данные во внутренний вектор (но для примера это опущено). Затем я реализую IntoIterator и Iterator для типа IntoIterator:

struct EntryOwned(u32);
struct Entry<'a>(&'a u32);

impl<'a> EntryOwned {
    fn to_entry(&'a self) -> Entry<'a> {
        Entry(&self.0)
    }
}

struct LazyStruct {
    cache: Vec<EntryOwned>,
}

impl<'a> LazyStruct {
    fn new(data: Vec<EntryOwned>) -> LazyStruct {
        Self {
            cache: data,
        }
    }

    fn get_entry(&'a self, index: usize) -> Option<Entry<'a>> {
        match self.cache.get(index) {
            Some(entry_owned) => Some(entry_owned.to_entry()),
            None => None,
        }
    }
}

impl<'a> IntoIterator for &'a mut LazyStruct {
    type Item = Entry<'a>;
    type IntoIter = LazyStructIter<'a>;

    fn into_iter(self) -> Self::IntoIter {
        LazyStructIter {
            inner: self,
            current_index: 0,
        }
    }
}

struct LazyStructIter<'a> {
    inner: &'a mut LazyStruct,
    current_index: usize,
}

impl<'a> LazyStructIter<'a> {
    fn next_item(&'a mut self) -> Option<Entry<'a>> {
        if self.current_index > self.inner.cache.len() {
            return None;
        }
        let ret = self.inner.get_entry(self.current_index);
        self.current_index += 1;
        ret
    }
}

impl<'a> Iterator for LazyStructIter<'a> {
    type Item = Entry<'a>;

    fn next(&mut self) -> Option<Self::Item> {
        self.next_item()
    }
}

, что приводит к проблеме на всю жизнь:

   Compiling playground v0.0.1 (/playground)
error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
  --> src/main.rs:64:14
   |
64 |         self.next_item()
   |              ^^^^^^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 63:5...
  --> src/main.rs:63:5
   |
63 | /     fn next(&mut self) -> Option<Self::Item> {
64 | |         self.next_item()
65 | |     }
   | |_____^
note: ...so that reference does not outlive borrowed content
  --> src/main.rs:64:9
   |
64 |         self.next_item()
   |         ^^^^
note: but, the lifetime must be valid for the lifetime `'a` as defined on the impl at 60:6...
  --> src/main.rs:60:6
   |
60 | impl<'a> Iterator for LazyStructIter<'a> {
   |      ^^
note: ...so that the types are compatible
  --> src/main.rs:64:14
   |
64 |         self.next_item()
   |              ^^^^^^^^^
   = note: expected  `&mut LazyStructIter<'_>`
              found  `&mut LazyStructIter<'a>`

Время жизни ссылок должно быть привязано на LazyStruct, но я не могу изменить черту Iter, не принимаю никаких указателей времени жизни. Я уже проверил некоторые ответы на подобные проблемы: Итератор, возвращающий элементы по ссылке, вопрос времени жизни Как мне написать итератор, который возвращает ссылки на себя?

EDIT : Entry является более сложной структурой данных, и ее невозможно скопировать. Я должен связать его с определенным временем жизни c, так как оно содержит ссылки на вещи.

Один из них указывает, что итератор должен хранить ссылку на исходную коллекцию вместо того, чтобы владеть ею, поскольку ему не разрешено вернуть ссылку с собственной жизнью. Но я не мог заставить это работать. Итак, как мне играть здесь ссылки?

Вот пример игровая площадка

Любая помощь приветствуется, спасибо всем заранее!

Ответы [ 2 ]

1 голос
/ 23 марта 2020

Вы не можете.

Требуется, чтобы у каждого элемента, возвращаемого next(), была ссылка со временем жизни, связанным с существованием итератора LazyStructIter.

Это так невозможно с интерфейсом Iterator:

pub trait Iterator {
    type Item;
    fn next(&mut self) -> Option<Self::Item>;

Но было бы возможно с чертой, определенной как:

trait FakeIterator<'a> {
    type Item;

    fn next(&'a mut self) -> std::option::Option<Self::Item>;
}

impl<'a> FakeIterator<'a> for LazyStructIter<'a> {
    type Item = Entry<'a>;

    fn next(&'a mut self) -> Option<Self::Item> {
        self.next_item()
    }
}

Благодаря возможности иметь своего рода итератор, который возвращает заимствование элементов от себя - цель RF C 1598 - GAT .

См. также эту статью для сбора обходных путей по этой теме c.

0 голосов
/ 21 марта 2020

Как правило, вам не нужно указывать время жизни в ржавчине, если только одна из двух вещей верна, ссылка привязана к владельцу, который не известен, или ссылка, которая установлена ​​c. Раст говорит, что ожидал '_ срок службы, что означает, что он понимает право собственности и заимствует самостоятельно.

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=c54f5f2f24e635dcfaa0fcbf69fa4ce0

struct EntryOwned(u32);
struct Entry(u32);

impl EntryOwned {
    fn to_entry(&self) -> Entry {
        Entry(self.0)
    }
}

struct LazyStruct {
    cache: Vec<EntryOwned>,
}

impl LazyStruct {
    fn new(data: Vec<EntryOwned>) -> LazyStruct {
        Self {
            cache: data,
        }
    }

    fn get_entry(&self, index: usize) -> Option<Entry> {
        match self.cache.get(index) {
            Some(entry_owned) => Some(entry_owned.to_entry()),
            None => None,
        }
    }
}

impl IntoIterator for LazyStruct {
    type Item = Entry;
    type IntoIter = LazyStructIter;

    fn into_iter(self) -> Self::IntoIter {
        LazyStructIter {
            inner: self,
            current_index: 0,
        }
    }
}

struct LazyStructIter {
    inner: LazyStruct,
    current_index: usize,
}

impl LazyStructIter {
    fn next_item(&mut self) -> Option<Entry> {
        if self.current_index > self.inner.cache.len() {
            return None;
        }
        let ret = self.inner.get_entry(self.current_index);
        self.current_index += 1;
        ret
    }
}

impl Iterator for LazyStructIter {
    type Item = Entry;

    fn next(&mut self) -> Option<Self::Item> {
        self.next_item()
    }
}


fn main() {
    // let json_data = r#"{"item_1" : 10, "item_2" : 100, "item_3" : 1000}"#;
    // if let Ok(data) = serde_json::from_str::<A>(json_data) {
    //     println!("{:?}", data);
    // }
    // else if let Ok(data) = serde_json::from_str::<B>(json_data) {
    //     println!("{:?}", data);
    // }
    for _ in 0..0 {
        println!("foo");    
    }
}

После удаления спецификаций срока службы , он прекрасно компилируется и запускается.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...