`AsRef` and lifetimes

I tried something like this today, and it failed to compile:

fn ret_ref<'a>(x:&'a mut Vec<u8>)->&'a[u8]{
    x.as_ref()
}
error[E0515]: cannot return value referencing function parameter `x`
 --> src/lib.rs:2:5
  |
2 |     x.as_ref()
  |     -^^^^^^^^^
  |     |
  |     returns a value referencing data owned by the current function
  |     `x` is borrowed here

This however works fine:

fn ret_ref<'a>(x:&'a mut Vec<u8>)->&'a[u8]{
    (&*x).as_ref()
}

I am guessing that one refers to <&Vec<u8> as AsRef> and the other to <&mut Vec<u8> as AsRef>.

Indeed, this also fails:

fn ret_ref<'a>(x:&'a mut Vec<u8>)->&'a[u8]{
    (&mut *x).as_ref()
}

Why are they different?

When performing method resolution for x.as_ref(), it will try to match up self: &Self with one of these "candidates", in order:

&mut Vec<u8>,  & &mut Vec<u8>, &mut &mut Vec<u8>,
     Vec<u8>,  &      Vec<u8>, &mut      Vec<u8>,
        [u8],  &         [u8], &mut         [u8],

The first one that matches is &&mut Vec<u8>, which matches up with self: &Self when Self is &mut Vec<u8>. So, you call

// note the auto-ref                  v
<&mut Vec<u8> as AsRef<[u8]>>::as_ref(&x)

But the &mut Vec<u8> is a local variable (x), and you can't return the borrow of a local variable.

Same thing for (&mut *x).as_ref().

When performing method resolution for (&*x).as_ref(), where the . operand is a &Vec<u8>, the candidates are:

&Vec<u8>,  & &Vec<u8>, &mut &Vec<u8>,
 Vec<u8>,  &  Vec<u8>, &mut  Vec<u8>,
    [u8],  &     [u8], &mut     [u8],

And the first one matches this time, which corresponds to Self = Vec<u8>. So instead you call

// note: no auto-ref, we're passing the operand by value now
<Vec<u8> as AsRef<[u8]>>::as_ref(&*x)

And one more example, if you try:

(*x).as_ref()

The candidates are:

 Vec<u8>,  &  Vec<u8>, &mut  Vec<u8>,
    [u8],  &     [u8], &mut     [u8],

And the second one matches, again corresponding to Self = Vec<u8>, and you call

// Same resulting expression as last time, but the compiler added an auto-ref
// for you instead of you spelling it out with `(&*x)`
//                               v
<Vec<u8> as AsRef<[u8]>>::as_ref(&*x)
3 Likes

For that matter, the entire original function can use auto-(de)ref:

fn ret_ref<'a>(x:&'a mut Vec<u8>)->&'a[u8]{
    x
}
5 Likes

Okay, and yet one more method call example:

fn ret_ref<'a>(x:&'a mut Vec<u8>)->&'a[u8]{
    x.as_mut()
}

This works because the first candidate, &mut Vec<u8>, will match the self: &mut Self of AsMut with Self = Vec<u8>, and calls

// No auto-ref
<Vec<u8> as AsMut<[u8]>>::as_mut(x)

and then the returned &mut [u8] is coerced to a &[u8].

Just a nit because it came up in some other threads recently: this is deref coercion. (Mixing up deref coercion and the method resolution process can confuse people.)

2 Likes

Note that unless you have a T: AsRef constraint somewhere, you should essentially never be using the as_ref method from AsRef in std::convert - Rust. It's just going to cause you problems and make it more likely that an update will run into inference failures.

(In contrast, inherent as_ref methods like https://doc.rust-lang.org/std/option/enum.Option.html#method.as_ref are completely fine. It's just the one from the trait that's the problem.)

4 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.