I have a simple trait which has a function which accepts an associate type.
pub trait Pipe {
type Item;
fn push(&mut self, value: Self::Item);
}
Now I want to do something with a Pipe
that accepts references rather than owned values.
pub fn do_something_ref<T>(trait_inst: T)
where
T: Pipe<Item = &usize>
{
let val: usize = 5;
trait_inst.push(&val);
}
The references will not live longer than the caller context lifetime. Maybe the Pipe clones the value. Maybe it will just println it. But it will not be allowed to store it since that would violate the caller context lifetime.
However the code above give an error: error[E0637]: `&` without an explicit lifetime name cannot be used here
.
How can I make this work?
Here's my attempt.
So, it seems that higher ranked trait bounds (HRTB) are useful here. If I can say "P: Pipe<Item = &'a>
for all possible 'a
" then that would mean the Pipe would accept very short-lived references.
pub fn do_something_ref<T>(trait_inst: T)
where
for <'a> T: Pipe<Item = &'a usize>
{
let val: usize = 5;
trait_inst.push(&val);
}
I think this should work, but it doesn't compile either: error[E0582]: binding for associated type `Item` references lifetime `'a`, which does not appear in the trait input types
.
Is that error a bug?
Could be https://github.com/rust-lang/rust/issues/49601 ?
So it seems this error only happens because the 'a
isn't in a spot that the compiler recognizes as valid for for<'a>
. So I tried to find some workaround by adding an unused lifetime parameter or helper traits, and although I found some solutions which initially compiled, none actually work when you try to use them.
How can I get this to work how I want? Thanks!
for<'a>
doesn't apply here because that's universal quantification ("for all'a
") but you are asking for existential quantification ("for some'a
(to be determined byT
)"). The problem with that is the existential version is unsound, because a temporary reference insidedo_something_ref
cannot be proven to outlive "some'a
". – trentclItem
a type parameter instead of an associated type or lift the reference so the lifetime becomes part of the signature ofpush
. – trentclFn
traits are input (parameter) types, not output (associated) types, so a rough equivalent toFn(&'a X)
would bePipe<&'a usize>
, like in my first link. (Fn(&'a X)
is essentially sugar for not-yet-stabilizedFn<(&'a X,), Output = ()>
;Output
is an associated type butArgs
is a parameter) – trentclItem
effectively creates a new trait for each differentItem
. With lifetimes this gives the compiler more freedom in determining what the particular trait is allowed to do. As I understand it, this is why adding an explicit generic lifetime to the trait helps when the trait has an output type. – user4815162342