2
votes

I tested a small example with lifetimes but sometimes i can't understand the problem, like here:

struct Space<'r> {
    v: &'r mut usize,
    z: usize,
}

impl<'r> Space<'r> {
    fn new(v: &'r mut usize) -> Self {
        Self { v, z: 0 }
    }
    
    fn add(&'r mut self) {
        self.z += 1;
        *self.v = self.z
    }

    fn make_val(&'r mut self, v: u32) -> Val<'r> {
        Val::new(self, v)
    }
}

struct Val<'r> {
    space: &'r mut Space<'r>,
    v: u32,
}

impl<'r> Val<'r> {
    fn new(space: &'r mut Space<'r>, v: u32) -> Self {
        Self { space, v }
    }
}

impl<'r> Clone for Val<'r> {
    fn clone(&self) -> Self {
        self.space.add();
        
        Self { space: self.space, v: self.v }
    }
}

fn main() {
    let mut v = 0usize;
    let mut space = Space::new(&mut v);
    let mut val1 = space.make_val(1234);
    let mut val2 = val1.clone();
    
    println!("Res: {} - {}/{}", v, val1.v, val2.v);
}

(Playground)

There is 2 compilation errors, the error at line 36 is difficult to fix for me but the first one at line 34 is really strange, it say that the lifetime cannot outlive be the method call is internal, nothing go outside.

Can you explain me theses problem and how to solve them, please ?

1
In your clone method, are you intending to modify the original value?Solomon Ucko
You won't achieve cloning of mutable references anyway. You may try to play with RefCell 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
I tried here: play.rust-lang.org/…, but same problem.Corebreaker

1 Answers

3
votes

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.