9

I would like to be able to obtain references (both immutable and mutable) to the usize wrapped in Bar in the Foo enum:

use Foo::*;

#[derive(Debug, PartialEq, Clone)]
pub enum Foo {
    Bar(usize)
}

impl Foo {
    /* this works */
    fn get_bar_ref(&self) -> &usize {
        match *self {
            Bar(ref n) => &n
        }
    }

    /* this doesn't */
    fn get_bar_ref_mut(&mut self) -> &mut usize {
        match *self {
            Bar(ref mut n) => &mut n
        }
    }
}

But I can't obtain the mutable reference because:

n does not live long enough

I was able to provide both variants of similar functions accessing other contents of Foo that are Boxed - why does the mutable borrow (and why only it) fail with an unboxed primitive?

Peter Hall
  • 53,120
  • 14
  • 139
  • 204
ljedrz
  • 20,316
  • 4
  • 69
  • 97

2 Answers2

8

You need to replace Bar(ref mut n) => &mut n with Bar(ref mut n) => n.

When you use ref mut n in Bar(ref mut n), it creates a mutable reference to the data in Bar, so the type of n is &mut usize. Then you try to return &mut n of &mut &mut u32 type.

This part is most likely incorrect.

Now deref coercion kicks in and converts &mut n into &mut *n, creating a temporary value *n of type usize, which doesn't live long enough.

red75prime
  • 3,733
  • 1
  • 16
  • 22
  • 2
    That's still odd. Why isn't the behaviour the same for `get_bar_ref`? – Peter Hall May 20 '17 at 19:49
  • According to the nomicon link you gave, `&T` coerces to `*const T` and `&mut T` to `*mut T`. It is still unclear to me why mutability affects the life span of the variable. – Peter Hall May 20 '17 at 20:22
  • 1
    Interesting. According to documentation `&Bar(ref n) => &n` should expand to `&Bar(ref n) => &*(n.deref())`, but it doesn't compile. I suppose there's some compiler magic involved. – red75prime May 20 '17 at 20:24
  • It is probably have something to do with [reborrowing of mutable references](http://stackoverflow.com/questions/43036156/how-can-i-reborrow-a-mutable-reference-without-passing-it-to-a-function) – red75prime May 20 '17 at 20:43
  • 2
    I do not believe this has anything to do with deref coercions. – Shepmaster May 20 '17 at 20:46
  • 1
    Thanks, it works. I thought `n` was still not a reference and `ref` only avoided having it moved in the `match` branch; my `Box`ed cases also seemed to confirm this - `Foo(ref boxed) => &*boxed` gives the reference to the contents of the `Box`, I don't need an additional deref (`&**boxed`). I'll give the question a bit more time, maybe someone can explain the reason why it depends on mutability; otherwise, I'll accept this answer. – ljedrz May 20 '17 at 21:28
  • 4
    Deref coercions *cannot possibly* create temporaries. What's happening has be a difference between `&mut **&mut n` (which is what deref coercions will effectively result in on that example) and `&mut *n` (which *does* work). It's unlikely to be required for soundness (since both versions do the *exact same* operation), but due to invariance under mutable references the lifetimes likely get "tightened" more than they should be. Can't find it now but it's possible this issue has been reported before. – eddyb May 21 '17 at 13:48
  • 2
    @ljedrz If you have `Foo(ref boxed)`, then `*boxed: Box` and `**boxed: T`, so `&**boxed: &T`. *However*, `boxed: &Box` will happily coerce to `&T` (through deref coercions). `&*boxed: &Box` is equivalent to simply using `boxed`. – eddyb May 21 '17 at 14:09
7

These examples show the sample problem:

fn implicit_reborrow<T>(x: &mut T) -> &mut T {
    x
}

fn explicit_reborrow<T>(x: &mut T) -> &mut T {
    &mut *x
}

fn implicit_reborrow_bad<T>(x: &mut T) -> &mut T {
    &mut x
}

fn explicit_reborrow_bad<T>(x: &mut T) -> &mut T {
    &mut **&mut x
}

The explicit_ versions show what the compiler deduces through deref coercions.
The _bad versions both error in the exact same way, while the other two compile.

This is either a bug, or a limitation in how lifetimes are currently implemented in the compiler. The invariance of &mut T over T might have something to do with it, because it results in &mut &'a mut T being invariant over 'a and thus more restrictive during inference than the shared reference (&&'a T) case, even though in this situation the strictness is unnecessary.

eddyb
  • 764
  • 7
  • 7