Passed reference with explicit lifetime is never 'unborrowed'


#1

In the following code, the call to foo.set() never releases the borrowed reference to foo, so the call to foo.get() is disallowed (‘cannot borrow foo as immutable because it is also borrowed as mutable’).

If I simply remove the explicit lifetime annotation on the ‘self’ parameter to Foo::set(), everything works as expected ('fn set(&self, v: &‘a u32) {’).

Unfortunately the real function I am trying to write needs the explicit lifetime annotation on self to avoid ‘cannot infer an appropriate lifetime for autoref due to conflicting requirements’ errors.

I cannot for the life of me grok why specifying the lifetime should cause set() to hold onto a mutable reference. Any clues for the clueless?

(by the way, above code is available on rust playground at http://is.gd/gO3KwB).

PS: is this the best forum to pester with n00b questions like this? If there’s a better place, please redirect me.

struct Foo<'a> {
    value: &'a u32,
}

impl<'a> Foo<'a> {
    fn set(&'a mut self, v: &'a u32) {
        self.value = v;
    }
    
    fn get(&self) -> u32 {
        *self.value
    }
}
        
fn main() {
    let x: u32 = 616;
    let y: u32 = 42;
    
    {
        let mut foo = Foo{value: &x};
        
        foo.set(&y);
    
        println!("val = {}", foo.get());
    }
}

#2

Saying &'a mut self is explicitly telling the compiler that it has to keep a mutable reference to self for the same lifetime as the struct foo (since the struct is annotated with the same lifetime <'a>). You could perhaps annotate self in the other instance with a separate lifetime - i.e. fn set<'b>(&'b mut self, v: &'a u32) - but I can’t say for sure without knowing more!


#3

Saying &'a mut self is explicitly telling the compiler that it has to keep a mutable reference.

Hmm, well, there’s the misunderstanding: what I want to tell the compiler is that it currently has a mutable ref with the specified lifefime (within the scope of set());. But I don’t want it to keep one indefinitely for me.

Is there some way to express that intention?

(I have found a way to work around the original problem, so this is not critical, but I still find the behaviour surprising).


#4

Well, your solution is exactly that: fn set(&mut self, v: &'a u32) (or more explicitly fn set<'b>(&'b mut self, v: &'a u32)) - since no lifetime is specified for self the compiler infers it to be the duration of the call.

Usually the ‘cannot infer an appropriate lifetime for autoref due to conflicting requirements’ error message is followed by the list of requirements.


#5

Thanks for the explanation. I’m going to have to do some more work to come up with a sensible example that demonstrates the original woe.

But I think what I actually want to do is have a function like

fn set<'b>(&'b mut self, v: &'a val)

where it is somehow explicit that 'b is a proper subset of 'a, so that I can safely deference 'a references in a 'b context. If that makes sense.

I can’t seem to find any mechanism for expressing the relationship of various lifetimes; does such a thing exist?

Anyway, thanks again for the help - I may return with a more complicated example later, but the immediate problem is fixed.


#6

This is a fine place to ask n00b questions. Depending on your preferences, StackOverflow can also be a good place to do so.

Since 'b is a reference to self, which contains a reference of lifetime 'a, it is already necessary that 'b is a proper subset of 'a, or that 'a outlives 'b. If this weren’t the case, then you could have an &'b self reference, and try to dereference self.value, and hit undefined behavior as you were dereference a value that was no longer live. So when you write the above example, the borrow checker has already that 'a outlives 'b.

There is a notation for making this explicit, which can be useful in some circumstances, though it isn’t necessary here:

fn set<'b>(&'b mut self, v: &'a u32) where 'a: 'b

Read : in this context as “outlives”: "'a outlives 'b".

This is perfectly correct to write, but it gives no more information than the borrow checker was already able to infer, so most people wouldn’t write this. However, I mention it since you say your original problem was more complex, so it may have needed such an explicit relationship provided.

In fact, in this case lifetime elision can allow you to write this even more succinctly; there’s an implicit lifetime parameter added for every input reference, so you don’t have to write it out explicitly:

fn set(&mut self, v: &'a u32)