Inherit trait from `From<&'_ [T]>` without specifying lifetime

Viewed 37

TL;RD. I would like to inherit a trait from From like this: pub trait VectorLike<'a, T: 'a + Clone>: From<&'a [T]> {} but in such a way that VectorLike doesn't have a lifetime parameter.

Loner version. I'm writing generic code that supports various vector kinds (e.g. standard Vec and SmallVec from the smallvec crate). In order for my functions to work with different vectors I'm making a trait that encompasses everything that is common to them. It goes like this:

pub trait VectorLike<T: Clone>:
    ops::Deref<Target = [T]> +
    IntoIterator<Item = T> +
    FromIterator<T> +
{
    fn pop(&mut self) -> Option<T>;
    fn push(&mut self, value: T);
}

Everything above works fine.

However I run into problem when I try to support creating vector from slices like this: Vec::from(&[1, 2, 3][..]). In order to allow this Vec implements From<&'_ [T]> trait. So naturally I'm trying to add it as a parent trait for VectorLike:

pub trait VectorLike<T: Clone>:
    // ...
    From<&[T]> +
{
    // ...
}

... and I get “expected named lifetime parameter” error. Rust compiler suggests to fix the code like this:

pub trait VectorLike<'a, T: 'a + Clone>:
    // ...
    From<&'a [T]> +
{
    // ...
}

Now I have to specify explicit lifetime wherever VectorLike is used. This seems completely unnecessary:

  • This lifetime contains no valuable information: the vector does not inherit the lifetime, it copies all elements.
  • Lifetime specification of this sort is not required when Vec is used directly, e.g. this works: fn make_vector(elements: &[i32]) -> Vec<i32> { Vec::<i32>::from(elements) }.
  • I can workaround this limitation by adding a new function instead of implementing From: pub trait VectorLike<T: Clone>: /* ... */ { fn from_slice(s: &[T]) -> Self; }. This way my trait is functionally equivalent and can be used without lifetime specifiers.

So is there a way to remove the superfluous lifetime specifier here?

P.S. For now I'm using the “new function” option as a workaround, but it has drawbacks. For example, I find it confusing when the function has a different name, but giving it the same name leads to ambiguity which needs to be resolved with verbose constructs like this: <SmallVec::<[T; 8]> as From<&[T]>>::from(slice).

1 Answers

Every reference in Rust must have an associated lifetime. The reason you usually don't have to write them out is because the compiler is very good at lifetime elision. Whenever this doesn't work, you need to carefully consider where the lifetime you need is actually coming form. In this case, the constraint you're trying to impose can be though of as "for any lifetime 'a that you give me, VectorLike<T> implements From<&'a [T]>". This is a higher-ranked trait bound that can be expressed using for<'a> syntax:

pub trait VectorLike<T: Clone>:
    ...
    for<'a> From<&'a [T]>
{
    ...
}

Playground

Related