Reputation: 26539
Code:
trait MyTrait {
fn update(&self, _prev: &Self) { unimplemented!() }
}
trait MyOtherTrait<'a> {}
pub struct MyTraitDynWrapper<'a> {
// When v1 commented out, code works
v1: &'a dyn MyOtherTrait<'a>,
v2: &'a (),
}
impl<'a> MyTrait for MyTraitDynWrapper<'a> {}
struct Container;
impl Container {
// GENERATED BY PROC MACRO: CANNOT CHANGE
fn with_ref<'outer_borrow, ReturnType>(
&'outer_borrow self,
user: impl for<'this> FnOnce(&'outer_borrow MyTraitDynWrapper<'this>) -> ReturnType,
) -> ReturnType {
unimplemented!()
}
}
fn main() {
let a = Container;
let b = Container;
a.with_ref(|a| {
b.with_ref(|b| {
MyTrait::update(a, b)
})
});
}
When compiling as-is, I get a lifetime errors complaining, in main
, that the lifetime of b
in the closure may not outlive the lifetime of a
, even though it should be fine since the call doesn't borrow anything for longer than the call.
If I comment out the v1
field of MyTraitDynWrapper
, the code compiles fine.
Container::with_ref
is a simplification of a method that the ouroboros crate generates (its similar to rental), and I cannot change it.
What's happening with this code and how can I fix the error while still being able to use the trait object?
Error:
error[E0308]: mismatched types
--> src/lib.rs:33:23
|
33 | MyTrait::update(a, b)
| ^ lifetime mismatch
|
= note: expected reference `&MyTraitDynWrapper<'_>`
found reference `&MyTraitDynWrapper<'_>`
note: the anonymous lifetime #2 defined on the body at 32:14...
--> src/lib.rs:32:14
|
32 | b.with_ref(|b| {
| ____________________^
33 | | MyTrait::update(a, b)
34 | | })
| |_________^
note: ...does not necessarily outlive the anonymous lifetime #2 defined on the body at 31:13
--> src/lib.rs:31:13
|
31 | a.with_ref(|a| {
| ________________^
32 | | b.with_ref(|b| {
33 | | MyTrait::update(a, b)
34 | | })
35 | | });
| |_____^
error[E0308]: mismatched types
--> src/lib.rs:33:23
|
33 | MyTrait::update(a, b)
| ^ lifetime mismatch
|
= note: expected reference `&MyTraitDynWrapper<'_>`
found reference `&MyTraitDynWrapper<'_>`
note: the anonymous lifetime #2 defined on the body at 31:13...
--> src/lib.rs:31:13
|
31 | a.with_ref(|a| {
| ________________^
32 | | b.with_ref(|b| {
33 | | MyTrait::update(a, b)
34 | | })
35 | | });
| |_____^
note: ...does not necessarily outlive the anonymous lifetime #2 defined on the body at 32:14
--> src/lib.rs:32:14
|
32 | b.with_ref(|b| {
| ____________________^
33 | | MyTrait::update(a, b)
34 | | })
| |_________^
error: aborting due to 2 previous errors
Upvotes: 1
Views: 217
Reputation: 27885
&'a dyn Trait<'a>
has many the same problems &'a mut Struct<'a>
does. Because traits (and therefore trait objects) are invariant over their lifetime parameters, once you put 'a
in a trait, the compiler can no longer vary it to try to satisfy lifetime constraints.
You cannot tell the compiler that a trait is covariant, but if you can't simply remove the lifetime parameter from MyOtherTrait
entirely, you might use a higher-ranked trait bound (HRTB) to say that MyOtherTrait
is not parameterized with 'a
within MyTraitDynWrapper<'a>
:
pub struct MyTraitDynWrapper<'a> {
v1: &'a dyn for<'b> MyOtherTrait<'b>,
v2: &'a (),
}
If not for the constraint that you can't change with_ref
, you could make 'a
and 'b
both parameters of MyTraitDynWrapper
:
pub struct MyTraitDynWrapper<'a, 'b> {
v1: &'a dyn MyOtherTrait<'b>,
v2: &'a (),
}
// ...
impl Container {
// GENERATED BY PROC MACRO: CANNOT CHANGE
fn with_ref<'outer_borrow, 'that, ReturnType>(
&'outer_borrow self,
user: impl for<'this> FnOnce(&'outer_borrow MyTraitDynWrapper<'this, 'that>) -> ReturnType,
) -> ReturnType {
unimplemented!()
}
}
However, if none of these options work for you, it's not clear what else you could do that would be sound. Bear in mind that traits can be implemented by all kinds of types, so the compiler is protecting you not just from your own code but from any code that might be written.
Upvotes: 1