Not sure I can explain this really well but here's one way of looking at it.
pub trait ValuedArray<T> {
fn value(&self, i: usize) -> T;
"If a type implements ValuedArray<T>
, you can borrow it (with any lifetime) to get a T
."
impl ValuedArray<i32> for Int {
fn value(&self, i: usize) -> i32 {
"Int
implements ValuedArray<i32>
, so you can borrow an Int
(with any lifetime) to get an i32
."
impl ValuedArray<&[u8]> for Binary {
// is equivalent to:
impl<'a> ValuedArray<&'a [u8]> for Binary {
"Binary
implements ValuedArray<&'a [u8]>
, for any lifetime 'a
, so you should be able to borrow a Binary
(with any lifetime) to get a &'a [u8]
; but..."
fn value(&self, i: usize) -> &[u8] {
// is equivalent to:
fn value<'b>(&'b self, i: usize) -> &'b [u8] {
"... in fact you can only borrow a Binary
to get a &'b [u8]
of one particular lifetime 'b
(i.e., the lifetime of the borrow of self
)."
If you annotate all the lifetimes explicitly, you do get a slightly better help message:
= note: expected `fn(&Binary, _) -> &'a [u8]`
found `fn(&Binary, _) -> &[u8]`
You can look up the lifetime elision rules to understand how the compiler decides what lifetimes things have when you don't specify any.
To solve your problem, you need to tell the compiler how the input lifetime is really related to the output lifetime. You can do that in the trait itself by limiting value
to only returning references:
pub trait ValuedArray<T: ?Sized> { // `?Sized` is necessary to allow `T` to be `[u8]`
fn value(&self, i: usize) -> &T; // bake in the reference assumption here
}
struct Binary {
a: Vec<u8>
}
impl ValuedArray<[u8]> for Binary {
fn value(&self, i: usize) -> &[u8] {
&self.a[i..i+1]
}
}
Or you can do it in the impl
, by implementing the trait for references so the lifetime is nameable when you implement it:
pub trait ValuedArray<T> {
fn value(self, i: usize) -> T;
}
struct Binary {
a: Vec<u8>
}
impl<'a> ValuedArray<&'a [u8]> for &'a Binary {
fn value(self, i: usize) -> &'a [u8] {
&self.a[i..i+1]
}
}
Either way you do it will also have implications for how you implement ValuedArray
for Int
. Without knowing anything special about your use case, I'd prefer the first thing.