Because you explicitly told the compiler that you have an &'a mut [u8] that is borrowed for 'a, i.e., for the entirety of its validity. You declared that T: Foo<'a>. Hence, dropping that mutable reference does not end the borrow. The lifetime 'a flows into the type parameter T because it is parameterized on the lifetime 'a.
trait Foo {
type Output<'a>: Foo;
fn new_foo<'a>(buf: &'a mut [u8]) -> Self::Output<'a>;
}
fn bar<T>(buf: &mut [u8])
where
T: Foo,
{
{
let x = T::new_foo(buf);
}
{
let y = T::new_foo(buf);
}
}
Still have some weird quirks though:
fn bar<T>(buf: &mut [u8])
where
T: Foo,
{
let mut x = T::new_foo(buf);
std::mem::drop(x); // Why is manually dropping this necessary??
x = T::new_foo(buf);
}
EDIT:
Ugh. Spoke too soon. I ran into a compiler bug on the actual code where it completely ignores the manual mem::drop. Here's the code for anyone who's curious.
EDIT2:
So it wasn't a compiler bug. Just an interaction with a potentially Drop type. Now I'm back to square one because I'm pretty sure there isn't a way to tell the compiler to limit a trait to non-Drop types.
// Won't compile
fn bar<T>(buf: &mut [u8])
where
T: Foo,
{
let mut x = T::new_foo(buf);
loop {
std::mem::drop(x);
x = T::new_foo(buf);
}
}
&'a mut [u8] explicitly forbids this. You are telling the compiler to stop you from allowing more than one call ever by adding 'a to the &'a mut, and that 'a outlives Foo, so everything is locked exclusively, once and only once, for as long as 'a exists.
'a on the trait means the loan must outlive the object, and mut is exclusive, so 'a mut means only one loan ever for the whole duration of the object's lifetime.
'b: 'a; won't save you in &mut case, because exclusive lifetimes are invariant, i.e. maximally inflexible and can't be implicitly shortened or extended, so 'b: 'a basically means "'b is exactly the same thing as 'a", and you're back to square one.
Don't put (forever-long) lifetimes on &mut self, and you won't have this limitation, because then every call to &mut self will create (reborrow) a new loan that is only temporary for the duration of the call, and most importantly, not as long as the 'a.
The function <T as PayloadReader>::new_reader creates an instance of the associated type Reader but that type is totally unrelated to T! It can be any type, the compiler has no idea that you always implement it as
Crucially, in the above trait, the lifetimes of new_reader and read_payload are totally unrelated lifetimes 'a and 'x. If you look at your impls, this fact should be obvious. This makes the trait basically useless.
I don't think that's what is implied, though. You cannot just conjure a loan from thin air, which is what "unrelated lifetimes" would do. You may want a shared reference with interior mutability which won't have the uniqueness constraint that &'a mut T has.
Not sure what you mean by "Not general enough". That impl is actually correct. If you're really convinced that it's not, feel free to share any errors you see.
What I mean to say is, I get that I need the output of new_foo to not be bound to &'a mut [u8] which is what I attempted to do with the higher-ranked trait bound and the GAT.
That's not what the GAT approach does though. The compiler is able to deduce the lifetime properly. Bound to &'a mut [u8] that is, which is why I'm back to square one.
Compiling playground v0.0.1 (/playground)
error: implementation of `Foo` is not general enough
--> src/lib.rs:26:5
|
26 | bar::<ConcreteFoo>(&mut buf);
| ^^^^^^^^^^^^^^^^^^ implementation of `Foo` is not general enough
|
= note: `Foo<'0>` would have to be implemented for the type `ConcreteFoo<'_>`, for any lifetime `'0`...
= note: ...but `Foo<'1>` is actually implemented for the type `ConcreteFoo<'1>`, for some specific lifetime `'1`
error: could not compile `playground` due to previous error
If instead it needs to be moved across threads, then you are looking for a different shared reference and interior mutability primitive like Arc<Mutex<T>>: Rust Playground (Note also that Tokio’s flavor of Mutex can be held across await points.)
And of course, if either of these are unacceptable it might be nice having that context.
Okay sorry I missed that one. I think my previous suggestion isn't really related to what you're trying to do. It seems you want to use a single type as a provider of a parametric family of traits. i.e. the call bar::<ConcreteFoo>(..) should mean for <'a> . ConcreteFoo<'a>, but this isn't expressible in rust, it requires higher kinded types (which are types that are not inhabited by values, but rather form other types).
Note that the only reason bar::<ConcreteFoo>() even parses correctly is due to lifetime elision. It really means bar::<ConcreteFoo<'_>>(). This is what the error tells you (roughly): the elided lifetime is the "for some specific lifetime '1" portion. The compiler helpfully names the elided lifetime for you by writing ConcreteFoo<'1>. In short, there is no such thing as "the type ConcreteFoo" - you cannot even name such a thing, let alone manipulate it.
Anyways, GATs may be appropriate here, but the Self type is not meaningful to the implementation (it's just a tag type), because you only want to use it to identify a lifetime parametric-family of types. Something like this