У вас есть итератор в Rust?

Мне нужно создать итератор, которому принадлежит значение (позволяет обернуть объект признака в Rc) и вернуть его как значение next() (площадка):

use std::rc::Rc;
use std::collections::HashMap;

trait TProduct {
    fn get_title(&self) -> String;
}

struct Product {
    title: String
}

impl<'a> TProduct for Product {
    fn get_title(&self) -> String { self.title }
}

trait TStorage<'a> {
    fn get_products(&self, key: &str) -> Option<Box<dyn Iterator<Item=Rc<dyn TProduct + 'a>> + '_>>;
}

struct Storage<'a> {
    products: HashMap<String, Vec<Rc<dyn TProduct + 'a>>>
}

impl<'a> TStorage<'a> for Storage<'a> {
    fn get_products(&self, key: &str) -> Option<Box<dyn Iterator<Item=Rc<dyn TProduct + 'a>> + '_>> {
        self.products.get(key)
            .map(|it| {
                let iter = it.into_iter(); // iter of &Rc, but we need owning iter (of Rc)
                let boxed_iter: Box<dyn Iterator<Item=Rc<dyn TProduct + 'a>>> = Box::new(iter); // problem here
                boxed_iter
            })
    }
}

fn main() {
    println!("Hello, world!");
}

Получаю следующее:

430 |                 let boxed_iter: Box<dyn Iterator<Item=Rc<dyn TProduct + 'a>>> = Box::new(iter);
    |                                                                                 ^^^^^^^^^^^^^^ expected struct `Rc`, found reference
    |
    = note: expected struct `Rc<(dyn TProduct + 'a)>`
            found reference `&Rc<dyn TProduct>`
    = note: required for the cast to the object type `dyn Iterator<Item = Rc<(dyn TProduct + 'a)>>`

Фактическая проблема заключается в том, что у меня есть еще один TStorage impl (на основе плоских буферов и владеющий vec равным TProduct), который возвращает Rc экземпляров (не ссылок), поэтому мне нужно адаптировать черту и эту сигнатуру impl.

Я понимаю, что текущий итератор заимствует объекты vec (и не перемещает их). Можно ли вернуть собственный итератор? Можно ли это сделать с помощью обертки / адаптера (который принимает ссылку на Rc и клонирует ее)?

Я попытался адаптировать flatbuffers impl для возврата итератора ссылок, но он ожидаемо не работает, поскольку он не может содержать временные созданные объекты:

&Rc::new(each_product)

Я думал об использовании Cow, но получаю другие сообщения об ошибках, связанных с временем жизни.

PS. Я пробовал клонировать оболочку (которая клонирует Rc и, таким образом, возвращает экземпляр, а не ссылку), но она не работает из-за времени жизни (игровая площадка):

// converts `&Rc` into `Rc`
struct CloningWrapper<'a> {
    iter: std::slice::Iter<'a, Rc<dyn TProduct + 'a>>
}

impl<'a> Iterator for CloningWrapper<'a> {
    type Item = Rc<dyn TProduct + 'a>;
    fn next(&mut self) -> Option<Self::Item> {
        self.iter.next().map(|it| {
            it.clone()
        })
    }
}

impl<'a> TStorage<'a> for Storage<'a> {
    fn get_products(&self, key: &str) -> Option<Box<dyn Iterator<Item=Rc<dyn TProduct + 'a>> + '_>> {
        self.products.get(key) // problem here
            .map(|it| {
                let iter = it.into_iter();
                let wrapped_iter = CloningWrapper { iter };
                let boxed_iter: Box<dyn Iterator<Item=Rc<dyn TProduct + 'a>>> = Box::new(wrapped_iter);
                boxed_iter
            })
    }
}

из-за следующего:

error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
  --> src/main.rs:40:23
   |
40 |         self.products.get(key)
   |                       ^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 39:5...
  --> src/main.rs:39:5
   |
39 |     fn get_products(&self, key: &str) -> Option<Box<dyn Iterator<Item=Rc<dyn TProduct + 'a>> + '_>> {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...so that reference does not outlive borrowed content

PPS2. Та же проблема на протяжении всего срока службы с Cow (детская площадка)


person 4ntoine    schedule 20.04.2021    source источник
comment
play.rust-lang.org/ Я просто исправлю ошибка компиляции я понятия не имею, что вы делаете   -  person Stargateur    schedule 20.04.2021


Ответы (1)


Как отмечает Stargateur, Iterator уже имеет встроенный адаптер клонирования: Iterator::cloned. Таким образом, вы можете просто использовать это, чтобы получить Iterator<Item=Rc<_>>, а затем преобразовать его в соответствующий объект признака:

impl<'a> TStorage<'a> for Storage<'a> {
    fn get_products(&self, key: &str) -> Option<Box<dyn Iterator<Item=Rc<dyn TProduct + 'a>> + '_>> {
        self.products.get(key)
            .map(|it| {
                Box::new(it.into_iter().cloned()) as Box<dyn Iterator<Item=_>>
            })
    }
}

Это в сторону iter() против into_iter() не имеет значения, ваш ввод - &Vec, поэтому вы получите _ 8_ в любом случае.

person Masklinn    schedule 20.04.2021
comment
было бы хорошо получить краткое объяснение разницы между .cloned() и моим домашним имплантом клонирования обертки. Должно быть что-то не так с определениями моих жизней. - person 4ntoine; 20.04.2021