简体   繁体   English

如何从具有关联类型的包装特征 object 获取特征 object?

[英]How do I obtain a trait object from a wrapper trait object with an associated type?

I have two traits Foo and Bar :我有两个特征FooBar

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 Foo s and Bar s but each Foo has the associated trait for a type of Bar .实际上,我会有许多不同类型的FooBar ,但每个Foo都有一种Bar类型的相关特征。 Keeping it simple for now, the SimpleFoo is associated with the SimpleBar :现在保持简单, SimpleFooSimpleBar相关联:

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 :在某些地方我可以使用 generics 和单态,但在某些地方我需要动态调度,比如这个 function 比需要一个dyn FooBar是一个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 :由于SimpleFoo实现Foo<B = SimpleBar>而不是Foo<B = dyn Bar>我不能直接传递它(我希望编译器或派生或其他东西可以在这里做魔术并使这成为可能),所以我有一个包装器 class它持有对某些特定Foo的引用,并且可以获得其特定的Bar并将其变成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.我不想在这里有任何 static 数据。 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 .我想将此处返回的&dyn Bar的生命周期与DynamicFooWrapper包装的foo的生命周期联系起来,因为&dyn Bar的生命周期至少与包装的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.例如,在Foo包装器上调用get_bar()之后,我什至想销毁Foo包装器,只要原始Foo项目还活着。 It should be possible since that guarantees the lifetime of Bar - I'm just not sure how to express this all.这应该是可能的,因为这保证了Bar的生命周期——我只是不确定如何表达这一切。

TL/DR: You need to use dyn Bar + 'a instead of plain dyn Bar : TL/DR:您需要使用dyn Bar + 'a而不是普通的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 .在某些时候, dyn Bar + 'a将与某个具体类型T匹配。 The + 'a constraint tells the compiler that if T contains references, then these references live at least as long as 'a . + 'a约束告诉编译器,如果T包含引用,那么这些引用至少与'a一样长。 This is required if you want to take a reference to T with lifetime 'a as in &'a Self::B .如果你想引用T的生命周期'a就像在&'a Self::B中那样,这是必需的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM