Reputation:
I'm starting from a root Fmt
and descending recursively (along a Vec<String>
) into Fmt
s produced by a get_subfmt
call. When the vector is empty, I call a method (not shown here for brevity). Each preceding Fmt
(and its BoxOrRef
) has naturally greater lifetime enforced by the recursive function's scope than the following one. This seems safe, but I'm very new to the lifetimes business, so I may have erred in my reasoning somewhere.
Consider the following bit of simplified code:
use std::borrow::Borrow;
pub trait Fmt {
fn get_subfmt<'a>(&'a self, name: &str) -> Option<BoxOrRef<'a, dyn Fmt>>;
}
pub enum BoxOrRef<'a, T: ?Sized + 'a> {
Boxed(Box<T>),
Ref(&'a T)
}
impl<'b, T: Borrow<dyn Fmt + 'b>> Fmt for T {
fn get_subfmt(&'b self, name: &str) -> Option<BoxOrRef<'b, dyn Fmt>> {
self.borrow().get_subfmt(name)
}
}
This fails with the following errors:
error[E0308]: method not compatible with trait
--> src/lib.rs:13:5
|
13 | fn get_subfmt(&'b self, name: &str) -> Option<BoxOrRef<'b, dyn Fmt>> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetime mismatch
|
= note: expected type `fn(&'a T, &str) -> std::option::Option<BoxOrRef<'a, Fmt + 'a>>`
found type `fn(&'b T, &str) -> std::option::Option<BoxOrRef<'b, Fmt + 'b>>`
note: the lifetime 'a as defined on the method body at 13:5...
--> src/lib.rs:13:5
|
13 | fn get_subfmt(&'b self, name: &str) -> Option<BoxOrRef<'b, dyn Fmt>> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...does not necessarily outlive the lifetime 'b as defined on the impl at 12:1
--> src/lib.rs:12:1
|
12 | impl<'b, T: Borrow<dyn Fmt + 'b>> Fmt for T {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0308]: method not compatible with trait
--> src/lib.rs:13:5
|
13 | fn get_subfmt(&'b self, name: &str) -> Option<BoxOrRef<'b, dyn Fmt>> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetime mismatch
|
= note: expected type `fn(&'a T, &str) -> std::option::Option<BoxOrRef<'a, Fmt + 'a>>`
found type `fn(&'b T, &str) -> std::option::Option<BoxOrRef<'b, Fmt + 'b>>`
note: the lifetime 'b as defined on the impl at 12:1...
--> src/lib.rs:12:1
|
12 | impl<'b, T: Borrow<dyn Fmt + 'b>> Fmt for T {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...does not necessarily outlive the lifetime 'a as defined on the method body at 13:5
--> src/lib.rs:13:5
|
13 | fn get_subfmt(&'b self, name: &str) -> Option<BoxOrRef<'b, dyn Fmt>> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
It seems that the compiler can't prove that the 'a
from the definition of get_subfmt
is the same as 'b
from the impl
, most likely because I haven't told it that it is. How do I communicate to the compiler that I want the self
reference to last precisely 'b
? I can't slap a <'a: 'b>
on the get_subfmt
in the impl
, as that doesn't match the method signature. I've tried constraining T
to be T: Borrow<dyn Fmt + 'b> + 'b
, but that doesn't help, the errors remain.
I could move 'a
from the method to the Fmt
itself, but it doesn't really seem right, so I would like to avoid it if there's a way to.
Upvotes: 1
Views: 143
Reputation: 65822
This is the best I could come up with:
use std::borrow::Borrow;
pub trait Fmt<'b> {
fn get_subfmt<'a>(&'a self, name: &str) -> Option<BoxOrRef<'a, dyn Fmt + 'a>>
where
'b: 'a;
}
pub enum BoxOrRef<'a, T: ?Sized + 'a> {
Boxed(Box<T>),
Ref(&'a T)
}
impl<'b, T: Borrow<dyn Fmt<'b> + 'b>> Fmt<'b> for T {
fn get_subfmt<'a>(&'a self, name: &str) -> Option<BoxOrRef<'a, dyn Fmt + 'a>>
where
'b: 'a,
{
self.borrow().get_subfmt(name)
}
}
We want the bound 'b: 'a
('b
outlives 'a
), not 'a: 'b
, because a borrowed reference cannot outlive its referent.
Upvotes: 2