3

I have two traits Foo and Bar:

trait Bar {
    fn get_name(&self) -> &str;
}

trait Foo {
    type B: Bar + ?Sized;

    fn get_bar(&self) -> &Self::B;
}

In reality, I'll have many different types of Foos and Bars but each Foo has the associated trait for a type of Bar. Keeping it simple for now, the SimpleFoo is associated with the SimpleBar:

struct SimpleBar {
    name: String,
}

impl Bar for SimpleBar {
    fn get_name(&self) -> &str {
        &self.name
    }
}

struct SimpleFoo {
    bar: Rc<SimpleBar>,
}

impl Foo for SimpleFoo {
    type B = SimpleBar;

    fn get_bar(&self) -> &SimpleBar {
        &self.bar
    }
}

In some places I can use generics and monomorphism, but I need dynamic dispatch in some locations, like this function than needs a dyn Foo whose Bar is a dyn Bar:

fn some_func_that_needs_dyn_foo_returning_a_dyn_bar(foo: &dyn Foo<B = dyn Bar>) {
    // do stuff
}

Since SimpleFoo implements Foo<B = SimpleBar> not Foo<B = dyn Bar> I can't directly pass it (I wish the compiler or a derive or something could do magic here and make this possible), so I have a wrapper class which holds a reference to some specific Foo and can get its specific Bar and make it into a dyn Bar:

struct DynamicFooWrapper<'a, F: Foo> {
    foo: &'a F,
}

impl<'a, F> Foo for DynamicFooWrapper<'a, F>
where
    F: Foo,
    <F as Foo>::B: Sized,
{
    type B = dyn Bar;

    fn get_bar(&self) -> &'a Self::B {
        self.foo.get_bar()
    }
}

fn main() {
    let b = Rc::new(SimpleBar {
        name: "Bar101".to_owned(),
    });
    let f = SimpleFoo { bar: b.clone() };
    some_func_that_needs_dyn_foo_returning_a_dyn_bar(&DynamicFooWrapper { foo: &f })
}

It's unhappy about the return lifetimes is in the implementation of the wrapper:

error[E0310]: the associated type `<F as Foo>::B` may not live long enough
  --> src/main.rs:45:9
   |
45 |         self.foo.get_bar()
   |         ^^^^^^^^^^^^^^^^^^
   |
   = help: consider adding an explicit lifetime bound `<F as Foo>::B: 'static`...
   = note: ...so that the type `<F as Foo>::B` will meet its required lifetime bounds

For more information about this error, try `rustc --explain E0310`.

I don't want to have any static data here. I'd like to tie the lifetime of the &dyn Bar returned here to the lifetime of the foo which the DynamicFooWrapper wraps because that &dyn Bar will live at least as long as the wrapped Foo. For instance, after calling get_bar() on the Foo wrapper, I'd even like to destroy the Foo wrapper and as long as the original Foo item is alive. It should be possible since that guarantees the lifetime of Bar - I'm just not sure how to express this all.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • Could you send some playground code to work with? – muppi090909 Feb 22 '22 at 12:03
  • 2
    Would [this](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=4874b0f619e3ad39003c7cbcdced5cd9) solve your problem? – Jmb Feb 22 '22 at 13:01
  • *I don't want to have any static data here* — Read the dupes linked off [What does the 'static lifetime mean in a trait bound in a Rust future?](https://stackoverflow.com/q/48504103/155423) and [this TL;DR](https://stackoverflow.com/questions/48504103/what-does-the-static-lifetime-mean-in-a-trait-bound-in-a-rust-future#comment84004134_48504103). Are you sure you understand it the right way? – Shepmaster Feb 22 '22 at 18:42
  • Re: I wish the compiler or a derive or something could do magic here and make this possible, this might become possible with the work done for async fn in traits. See the series "Dyn async traits" from Niko (the last one is [here](https://smallcultfollowing.com/babysteps//blog/2022/01/07/dyn-async-traits-part-7/)). – Chayim Friedman Feb 22 '22 at 22:37
  • Thanks @Jmb , that works! But what does it mean do have the lifetime here: `fn some_func_that_needs_dyn_foo_returning_a_dyn_bar<'a>(_foo: &dyn Foo)`, the `dyn Bar +'a`. I know it won't won't matter compile-wise since all the lifetimes are erased but what does it "prove" to rustc? – Harrison Metzger Feb 22 '22 at 23:03

1 Answers1

1

TL/DR: You need to use dyn Bar + 'a instead of plain dyn Bar:

fn some_func_that_needs_dyn_foo_returning_a_dyn_bar<'a>(_foo: &dyn Foo<B=dyn Bar + 'a>) {
    // do stuff
}

struct DynamicFooWrapper<'a, F: Foo> {
    foo: &'a F,
}

impl<'a, F> Foo for DynamicFooWrapper<'a, F>
where
    F: Foo,
    <F as Foo>::B: Sized,
{
    type B = dyn Bar + 'a;

    fn get_bar(&self) -> &'a Self::B {
        self.foo.get_bar()
    }
}

fn main() {
    let b = Rc::new(SimpleBar {name: "Bar101".to_owned()});
    let f = SimpleFoo { bar: b.clone() };
    some_func_that_needs_dyn_foo_returning_a_dyn_bar(&DynamicFooWrapper{foo: &f})
}

Playground

At some point the dyn Bar + 'a will be matched with some concrete type T. The + 'a constraint tells the compiler that if T contains references, then these references live at least as long as 'a. This is required if you want to take a reference to T with lifetime 'a as in &'a Self::B.

Jmb
  • 18,893
  • 2
  • 28
  • 55