user6564029
user6564029

Reputation:

How to specify that a lifetime from an impl should be the same as the lifetime of 'self'?

I'm starting from a root Fmt and descending recursively (along a Vec<String>) into Fmts 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

Answers (1)

Francis Gagn&#233;
Francis Gagn&#233;

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

Related Questions