I was wondering if there is any way to fix the function signature to make it compile?
Currently all that I managed to do is either:
Use the concrete type as argument (which defeats the whole purpose of the trait abstraction)
Change the function's signature to accept a mutable reference to T instead of taking ownership of T. Maybe this could somehow be enough for my project? I have a hunch that somewhere along the way I might need to take ownership over object.
Compiler error:
error[E0309]: the parameter type `T` may not live long enough
--> src/main.rs:44:5
|
43 | fn func2<'a, T: MyTrait<'a>>(mut a: T) {
| -- help: consider adding an explicit lifetime bound...: `T: 'a +`
44 | a.get_my_type();
| ^ ...so that the type `T` is not borrowed for too long
error[E0309]: the parameter type `T` may not live long enough
--> src/main.rs:44:7
|
43 | fn func2<'a, T: MyTrait<'a>>(mut a: T) {
| -- help: consider adding an explicit lifetime bound...: `T: 'a +`
44 | a.get_my_type();
| ^^^^^^^^^^^ ...so that the reference type `&'a mut T` does not outlive the data it points at
For reference, I include a full self contained example:
trait MyTrait<'a> {
type MyType;
fn get_my_type(&'a mut self) -> Self::MyType;
}
struct MyStruct {
num: u32,
}
struct MyTypeStruct<'a> {
num: &'a mut u32,
}
impl<'a> MyTrait<'a> for MyStruct {
type MyType = MyTypeStruct<'a>;
fn get_my_type(&'a mut self) -> Self::MyType {
MyTypeStruct { num: &mut self.num }
}
}
// Compiles when specifying the struct name directly.
fn func1(mut a: MyStruct) {
a.get_my_type();
}
// Does not compile:
fn func2<'a, T: MyTrait<'a>>(mut a: T) {
a.get_my_type();
}
fn main() {
let mut my_struct = MyStruct { num: 3 };
let my_type_struct = my_struct.get_my_type();
*my_type_struct.num = 4;
println!("{}", my_struct.num);
}
Edit
Here as an example for a function that borrows T instead of taking ownership:
The problem is that the lifetime parameter of func2 does not match up with the lifetime of the borrow of the local variable a. In particular, lifetime parameters such as 'a will always refer to lifetimes that are longer than the duration of the function call, whereas the local variable can only be borrowed for shorter than the duration of the function call.
The solution that Rust offers is HRTBs (higher rank trait bounds). Page in the nomicon. Section in the reference. You just assert the trait bound T: MyTrait<'a> for all lifetimes 'a, and that way the lifetime of the short, local borrow is also applicable. The signature becomes
fn func2<T: for<'a> MyTrait<'a>>(mut a: T) {
a.get_my_type();
}
// alternative syntax with the `for` coming first, in where clauses
fn func2<T>(mut a: T)
where
for<'a> T: MyTrait<'a>
// you can still also write
// T: for<'a> MyTrait<'a>
// instead
{
a.get_my_type();
}
/// Trait used to write the `impl`s
trait MyTrait_<'a> {
type MyType /* : bounds… */ ; // You will probably need to add bounds here when doing HRTB…
fn get_my_type (self: &'a mut Self)
-> Self::MyType
;
}
/// Trait alias to be used in bounds.
trait MyTrait
where
for<'any> Self : MyTrait_<'any>,
{}
impl<T : ?Sized> MyTrait for T
where
for<'any> Self : MyTrait_<'any>,
{}
@steffahn your solution works. Is there a way to specify the lifetime requirement in the associated type itself? The gap in lifetimes is apparent in the trait definition.
In case that wasn’t clear, what @Yandros is suggesting is to introduce a shorthand/alias for the HRTB for<'a> MyTrait<'a>. Furthermore the suggestion was to name this shorthand MyTrait and name the original trait MyTrait_ instead.
The
trait MyTrait
where
for<'any> Self : MyTrait_<'any>,
{}
definition is equivalent to
trait MyTrait: for<'a> MyTrait<'a>
{}
so the HRTB becomes a “supertrait”, hence T: MyTrait bounds in a function imply that you have T: for<'a> MyTrait_<'a> available. The two bounds MyTrait and for<'a> MyTrait<'a> become fully equivalent by the additional “blanket implementation”
impl<T : ?Sized> MyTrait for T
where
for<'any> Self : MyTrait_<'any>,
{}
which is equivalent to
impl<T: ?Sized> MyTrait for T
where
T: for<'a> MyTrait_<'a>,
{}
this way, T: for<'a> MyTrait_<'a> also implies T: MyTrait.
This is analogous to what serde does, in the case of serde, DeserializeOwned is a shorthand for for<'de> Deserialize<'de>. You can see the supertrait and the blanket implementation in the docs (expand “Show declaration” to see supertraits).