Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does a reference not live long enough in case of "as_slice"?

Tags:

rust

I cannot figure out why this code compiles:

fn f(v: &mut Vec<isize>) -> &[isize] {
    v.as_mut_slice()
}

and this does not:

fn f(v: &mut Vec<isize>) -> &[isize] {
    v.as_slice()
}

producing:

<anon>:2:5: 2:6 error: `v` does not live long enough
<anon>:2     v.as_slice()
             ^
<anon>:1:38: 3:2 note: reference must be valid for the anonymous lifetime #1 defined on the block at 1:37...
<anon>:1 fn f(v: &mut Vec<isize>) -> &[isize] {
<anon>:2     v.as_slice()
<anon>:3 }
<anon>:1:38: 3:2 note: ...but borrowed value is only valid for the block at 1:37
<anon>:1 fn f(v: &mut Vec<isize>) -> &[isize] {
<anon>:2     v.as_slice()
<anon>:3 }

If I understand correctly, in either case function signature is same, and return value lifetime is equal to the input parameter one. So why "as_slice" does not work?

like image 584
swizard Avatar asked Oct 31 '22 10:10

swizard


1 Answers

it's a "bug" or better a limitation of the AsSlice trait. Since v.as_slice() is now unstable and will probably be removed in favor of &v[] (which already works as intended in your case) I will not open a bug, but I'll try explaining why the current trait does not work to the best of my knowledge.

First, look at the definition of the as_slice that is invoked in your case.

impl<'a, T, U: ?Sized + AsSlice<T>> AsSlice<T> for &'a mut U {
    #[inline(always)]
    fn as_slice(&self) -> &[T] { AsSlice::as_slice(*self) }
}

note that as_slice is actually eliding a new lifetime. If we give it a name ('b) we're actually writing something like:

impl<'a, T, U: ?Sized + AsSlice<T>> AsSlice<T> for &'a mut U {
    #[inline(always)]
    fn as_slice<'b>(&'b self) -> &'b [T] { AsSlice::as_slice(*self) }
}

What we would actually want is 'b to be the same as 'a, but I think there was no way to express this at the time AsSlice was created (now this might be possible with Higher Ranked Trait Bounds). The effect of this is that, when we call as_slice() in your function f, we're returning a fresh lifetime, that can't escape f. This is in fact the error you're getting.

If AsSlice was written now, it would be using associated types and would be able to link lifetimes in the way we want. It would be something similar to this:

pub trait AsSlice2 {
    type Item;
    fn as_slice_2(&self) -> & [Self::Item];
}

impl<T> AsSlice2 for [T] {
    type Item = T;
    fn as_slice_2(&self) -> &[T] { &self[] }
}

playpen

This is similar to how as_mut_slice is currently implemented (that's why that one works)

like image 69
Paolo Falabella Avatar answered Jan 04 '23 14:01

Paolo Falabella