Going through the parts that stand out to me, here are the potential changes I would make.
Structs
Going through the two structs you have, the first one looks reasonable.
struct Space<'r> {
v: &'r mut usize,
z: usize,
}
However, the Val
struct doesn't make as much sense to me. If the reference to Space
has the same lifetime 'r
as the Space
itself, then it is essentially equivalent to moving Val<'r>
into Space<'r>
. While not illegal, I would recommend changing it.
// Just claim Space<'r> completely
struct Val<'r> {
space: Space<'r>,
v: u32,
}
Another option would be to add an additional lifetime to Val<'r>
so that make_val
could be called more than once without causing lifetime conflicts.
// life of reference to space 'a <= 'r
struct Val<'a, 'r: 'a> {
space: &'a mut Space<'r>,
v: u32,
}
// Modify to allow for second lifetime to be used.
fn make_val<'a>(&'a mut self, v: u32) -> Val<'a, 'r> {
Val { space: self, v }
}
Space::add
The explicit lifetime on &'r mut self
is not necessary for the function to run. Of the two errors you get when attempting to run your code, the entire first error is just the compiler telling you why this won't work. It is over constraining the function and unnecessarily confusing the compiler so lets remove it.
fn add(&mut self) {
self.z += 1;
*self.v = self.z
}
The reason it was unable to resolve it was because &mut self
is really two lifetimes that you are requiring be equal. It may make more sense if we visualize all the lifetimes involved:
// add(&mut self) tells the compiler to infer a lifetime for 'a that doesn't exceed 'b
fn add<'a, 'b: 'a>(self: &'a mut Space<'b>) {}
// add(&'r mut self) tells the compiler that the reference must live as long as the data
fn add<'b>(self: &'b mut Space<'b>) {}
Cloning
Cloning something containing a mutable reference doesn't make sense. Rust very explicitly forbids having multiple mutable references to something at any given point in memory. Cloning the reference is technically possible, but would require unsafe code. You shouldn't ever do this, but I wrote it anyway below.
impl<'r> Clone for Val<'r> {
fn clone(&self) -> Val<'r> {
unsafe {
// Make self mutable so we can access self.space
let my_self: &mut Self = &mut *(self as *const _ as *mut _);
// Requires a mutable reference to self to run space::add
my_self.space.add();
Self {
// Duplicate reference via unsafe code
space: &mut *my_self.space,
v: self.v
}
}
}
}
Printing
Finally, the print line in main uses v
despite a mutable reference to it still being in use. This is fairly self explanatory so I won't go into much more detail.
Safe Rust
The safe way to solve this problem is to use Rc<Refcell<T>>
. The Rc
allows for multiple owned references to be taken across a single thread and the RefCell
allows interior mutability. RefCell
is able to do this by checking if any other references exist at runtime instead of compile time. Here is a version of the playground that works with a single RefCell
, but a second could also be used to better match your original code.
clone
method, are you intending to modify the original value? – Solomon UckoRefCell
to delay borrowing to runtime, but still, the Rust's approach is to avoid references at all. I don't any specific reason in your example not to use owned values. – Alexey Larionov