This is not the right way, and the compiler is right.
One way to understand why this is so is to look at the type (slice) and trait that, in your opinion, it implements side by side:
for<'a> IntoTest<'a, &'a [u8]> &'a [u8]
Here, for clarity, I added a parameter for the life expectancy per slice. You can see that, in relation to the value, the 'a bound lifetime parameter using the for classifier, in other words, this expression is self-sufficient, it does not depend on anything in the external area.
In the cut, however, the parameter of the lifetime is free - it is defined in some external region, for example, through a lifelong elision. Thus, there is simply no way for the slice to satisfy your implementation. For it to work, your trait must be implemented as follows:
impl<'a, 'b> IntoTest<'a, &'a [u8]> for &'b [u8] { fn into_test(&'a self) -> &'a [u8] { unimplemented!() } }
which compiles , although it does not and cannot do what you want. You can see that in this ad the slice lifetimes and in the signature of the attribute do not overlap, so now the fragments satisfy the for<'a> estimate.
There is another way to look at this issue. T: for<'a> IntoTest<'a, &'a [u8]> bound means that the type T implements the attribute for each possible parameter of the lifetime of this attribute, so the function itself can decide what lifetime it wants. For example, it may request 'static . But naturally, your &[u8] code is bound to a vector in the main method and cannot be 'static . Therefore, the sound is broken - this &[u8] cannot provide any service life that the user wants, and therefore it does not implement for<'a> IntoTest<'a, &'a [u8]> .
Note that simply adding a life span parameter to a common function will also not work:
fn higher_ranked_lifetime<'a, T>(test: T) where T: IntoTest<'a, &'a [u8]> { let _t = test.into_test(); }
This error will be:
<anon>:12:14: 12:18 error: `test` does not live long enough <anon>:12 let _t = test.into_test(); ^~~~ <anon>:11:79: 13:2 note: reference must be valid for the lifetime 'a as defined on the block at 11:78... <anon>:11 fn higher_ranked_lifetime<'a, T>(test: T) where T: IntoTest<'a, &'a [u8]> { <anon>:12 let _t = test.into_test(); <anon>:13 } <anon>:11:79: 13:2 note: ...but borrowed value is only valid for the scope of parameters for function at 11:78 <anon>:11 fn higher_ranked_lifetime<'a, T>(test: T) where T: IntoTest<'a, &'a [u8]> { <anon>:12 let _t = test.into_test(); <anon>:13 }
And that is right again. Remember that your trait is defined as follows:
pub trait IntoTest<'a, T> { fn into_test(&'a self) -> T where Self: Sized; }
Here you require that self be passed by reference with the same life cycle as the lifetime parameter for this attribute. However, the above declaration of a generic function makes this method not callable in principle:
fn higher_ranked_lifetime<'a, T>(test: T) where T: IntoTest<'a, &'a [u8]>
A sign is indicated here so that the lifetime parameter is equal to the lifetime parameter of the function. However, the test parameter itself only lives through the body of the function. Therefore, any reference made to test , including implicit when calling into_test() :
let _t = (&test).into_test();
will have a lifetime strictly less than the lifetime parameter, and therefore it cannot be used as a parameter for the into_test() method. This is exactly what we are talking about.
Since you have not explained what you really need, it is hard to say what you should do. I think that one of the most common ways would be to simply drop your lifetime parameter by attribute and force the attribute method to take self by value and fix the general function accordingly:
pub trait IntoTest<T> { fn into_test(self) -> T; } impl<'a> IntoTest<&'a [u8]> for &'a [u8] { fn into_test(self) -> &'a [u8] { self } } fn higher_ranked_lifetime<'a, T>(test: T) where T: IntoTest<&'a [u8]> { let _t = test.into_test(); } fn main() { println!("Hello, world!"); let vec = vec![1u8]; let slice = &vec[..]; higher_ranked_lifetime(slice); }
This one works because now the symptom itself has no life parameters and does not add any requirements on how to call it. Now the parameters of the lifetime move to the types that implement it.