I'm trying quite complex stuff with Rust where I need the following attributes, and am fighting the compiler.
- Object which itself lives from start to finish of application, however, where internal maps/vectors could be modified during application lifetime
- Multiple references to object that can read internal maps/vectors of an object
- All single threaded
- Multiple nested iterators which are map/modified in lazy manner to perform fast and complex calculations (see example below)
A small example, which already causes problems:
use std::cell::RefCell;
use std::rc::Rc;
use std::sync::Weak;
pub struct Holder {
array_ref: Weak<RefCell<Vec<isize>>>,
}
impl Holder {
pub fn new(array_ref: Weak<RefCell<Vec<isize>>>) -> Self {
Self { array_ref }
}
fn get_iterator(&self) -> impl Iterator<Item = f64> + '_ {
self.array_ref
.upgrade()
.unwrap()
.borrow()
.iter()
.map(|value| *value as f64 * 2.0)
}
}
get_iterator
is just one of the implementations of a trait, but even this example already does not work.
The reason for Weak
/Rc
is to make sure that multiple places points to object (from point (1)) and other place can modify its internals (Vec<isize>
).
What is the best way to approach this situation, given that end goal is performance critical?
EDIT: Person suggested using https://doc.rust-lang.org/std/cell/struct.Ref.html#method.map
But unfortunately still can't get - if I should also change return type - or maybe the closure function is wrong here
fn get_iterator(&self) -> impl Iterator<Item=f64> + '_ {
let x = self.array_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
}
IDEA say it has wrong return type
the trait `Iterator` is not implemented for `Ref<'_, Map<std::slice::Iter<'_, isize>, [closure@src/bin/main.rs:30:46: 30:65]>>`
This won't work because
self.array_ref.upgrade()
creates a local temporaryArc
value, but theRef
only borrows from it. Obviously, you can't return a value that borrows from a local.To make this work you need a second structure to own the
Arc
, which can implementIterator
in this case since the produced items aren't references:Alternatively, if you want the iterator to also weakly-reference the value contained within, you can have it hold a
Weak
instead and upgrade on eachnext()
call. There are performance implications, but this also makes it easier to haveget_iterator()
be able to return an iterator directly instead of anOption
, and the iterator written so that a failed upgrade means the sequence has ended:This will make it so that you always get an iterator, but it's empty if the
Weak
is dead. TheWeak
can also die during iteration, at which point the sequence will abruptly end.