Returning iterator from weak references for mapping and modifying values

Hello!

I am trying to create system where I can both modify and read from single object that is created at startup, and I can control that I only either read (and close reference afterwards) or write to it (i.e. modify Vec only in one place of code)

use std::cell::RefCell;
use std::rc::Rc;
use std::sync::Weak;
pub struct Holder {
    arrray_ref: Weak<RefCell<Vec<isize>>>,
}
  
impl Holder {
    pub fn new(array_ref: Weak<RefCell<Vec<isize>>>) -> Self {
        Self { arrray_ref: array_ref }
    }

    fn get_iterator<'a>(&'a self) -> impl Iterator<Item=isize> + 'a {
        self.arrray_ref.upgrade().unwrap().borrow().iter().map(|value| *value as f64 * 2.0)
    }
}

Which gets

error[E0515]: cannot return value referencing temporary value
  --> src/bin/main.rs:18:9
   |
18 |         self.core_quotes_ob.borrow().iter().copied().take(5)
   |         ----------------------------^^^^^^^^^^^^^^^^^^^^^^^^
   |         |
   |         returns a value referencing data owned by the current function
   |         temporary value created here
   |
   = help: use `.collect()` to allocate the iterator

The example is simplified, but nessesary, because in a more general core fn get_iterator is a Trait function with different implementations, and I want to chain iterators effectively using custom map function in each implementation of get_iterator to get generically chained results which can be configured differently

Note that I do not need to return references from iterators, because in every implementation of get_iterator, I return isize, and not &isize

Do I need to opt for unsafe maybe or some nightly features to get this iterators map chain thing compile and work?

Okay, with a help of people I realized that Ref in std::cell - Rust is actually the key to success here, but can't fully reporoduce working code, since return value is incorrect

    fn get_iterator(&self) -> impl Iterator<Item=f64> + '_ {
        let x = self.arrray_ref.upgrade().unwrap().borrow();
        let map1 = Ref::map(x, |x| &x.iter());
        let map2 = Ref::map(map1, |iter| &iter.map(|y| *y as f64 * 2.0));
        map2
    }

the trait `Iterator` is not implemented for `Ref<'_, Map<std::slice::Iter<'_, isize>, [closure@src/bin/main.rs:30:46: 30:65]>>`

Does my get_iterator function needs to return Ref as well so they can work further in code?

You can't really do this. Fundamentally, its often difficult to return something that references the interior of a RefCell. It's not going to work with Ref::map either.

That said, you can define your own iterator:

use std::cell::RefCell;
use std::rc::Rc;

struct MyIter {
    values: Rc<RefCell<Vec<isize>>>,
    idx: usize,
}

impl Iterator for MyIter {
    type Item = isize;
    fn next(&mut self) -> Option<isize> {
        match self.values.borrow().get(self.idx) {
            Some(value) => {
                self.idx += 1;
                Some(*value)
            },
            None => None,
        }
    }
}

What if MyIter should have Rc<RefCell<impl Iterator>> - i.e. generic Iterator? That won't work at all?

Are there unsafe options to handle this general problem?

Build your custom iterator on top of MyIter. For example:

    fn get_iterator(&self) -> impl Iterator<Item=f64> {
        let iter = MyIter { values: self.array_ref.upgrade().unwrap(), idx: 0 };
        iter.map(|value| value as f64 * 2.0)
    }

Thanks, im almost getting it. So in case i want to chain maps of say 4-10 iterators, which are all coming from some generic Trait (which implements iterators differently), and also use Weak/Rc abstractions (for outer code to work correctly as well) I need

  1. Some wrapper object where I would save Ref to underlying Trait implementation and implement iterator on this object
  2. Only use borrow of underlying of Ref (which is triggered in next() methods) in one place where I need final chained map to get final values

Am I correct more or less?

I don't really understand your description.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.