简体   繁体   English

将盒装特征 object 传递给 function 接受实现特征的通用参数

[英]Passing boxed trait object to function accepting generic parameter implementing the trait

I have a function which returns a boxed trait object, and another function that accepts a reference to an object implementing the same trait. I have a function which returns a boxed trait object, and another function that accepts a reference to an object implementing the same trait. I would like to pass a reference to the boxed trait object to the second function, but I am unable to figure out how to do this.我想将对盒装特征 object 的引用传递给第二个 function,但我无法弄清楚如何做到这一点。

Example simplified code:示例简化代码:

trait MyTrait {
    fn foo(&self);
}

struct A {}

impl MyTrait for A {
    fn foo(&self) {
        println!("A");
    }
}

struct B {}

impl MyTrait for B{
    fn foo(&self) {
        println!("B");
    }
}

enum MyEnum {
    A,
    B,
}

fn create_object(my_enum: MyEnum) -> Box<dyn MyTrait> {
    let boxed_value: Box<dyn MyTrait> = match my_enum {
        MyEnum::A => Box::new(A{}),
        MyEnum::B => Box::new(B{}),
    };
    boxed_value
}

fn do_something<T: MyTrait>(obj: &T) {
    obj.foo();
}

fn main() {
    use std::borrow::BorrowMut;
    let boxed_value = create_object(MyEnum::A);
    do_something(boxed_value.borrow_mut());
}

The error I get:我得到的错误:

error[E0282]: type annotations needed
  --> src\main.rs:42:5
   |
42 |     do_something(boxed_value.borrow_mut());
   |     ^^^^^^^^^^^^ ------------------------ this method call resolves to `&mut Borrowed`
   |     |
   |     cannot infer type for type parameter `T` declared on the function `do_something`  

Intuitively, I would have hoped that in this case Rust would use dynamic dispatch and wouldn't care about the concrete type T (similarly to what happens in C++ when you pass a reference to a base class), but this seems not to be the case.直观地说,我希望在这种情况下 Rust 将使用动态调度并且不关心具体类型 T(类似于 C++ 中发生的情况,当您传递对基类的引用时),但这似乎不是案子。

How do I pass a reference to the boxed trait object ( Box<dyn MyTrait> ) to the second function ( do_something )?如何将对盒装特征 object ( Box<dyn MyTrait> ) 的引用传递给第二个 function ( do_something )? Is this possible in some way?这在某种程度上可能吗? A solution requiring a change to do_something would also be acceptable.需要更改do_something的解决方案也是可以接受的。

Intuitively, I would have hoped that in this case Rust would use dynamic dispatch and wouldn't care about the concrete type T (similarly to what happens in C++ when you pass a reference to a base class), but this seems not to be the case.直观地说,我希望在这种情况下 Rust 将使用动态调度并且不关心具体类型 T(类似于 C++ 中发生的情况,当您传递对基类的引用时),但这似乎不是案子。

You can make that happen with a cast (or just type ascription, eventually) and by relaxing the default requirement for T to be Sized :您可以通过强制转换(或最终仅键入归属)并放宽TSized的默认要求来实现这一点:

fn do_something<T: MyTrait + ?Sized>(obj: &T) {
    obj.foo();
}
use std::borrow::Borrow;
let boxed_value = create_object(MyEnum::A);
do_something(boxed_value.borrow() as &dyn MyTrait);

But if you're not otherwise using T , you can opt into dynamic dispatch on the function side much more simply:但是,如果您不以其他方式使用T ,则可以更简单地选择 function 端的动态调度:

fn do_something(obj: &dyn Borrow) {
    obj.foo();
}
use std::borrow::Borrow;
let boxed_value = create_object(MyEnum::A);
do_something(boxed_value.borrow());

And if you don't care that obj is a borrow and want to leave the option of static dispatch open, you can implement MyTrait for &dyn MyTrait :如果您不关心obj是借用的,并且想要保留 static 调度的选项打开,您可以为&dyn MyTrait MyTrait

impl MyTrait for &dyn MyTrait {
    fn foo(&self) {
        (*self).foo();
    }
}
fn do_something<T: MyTrait>(obj: T) {
    obj.foo();
}

// or, again, if not otherwise using T:

fn do_something(obj: impl MyTrait) {
    obj.foo();
}
use std::borrow::Borrow;
let boxed_value = create_object(MyEnum::A);
do_something(boxed_value.borrow());

No matter what, you'll need to add ?Sized to the trait bound in do_something , and then I think you have one of three options:无论如何,您都需要将?Sized添加到do_something中的特征绑定,然后我认为您有以下三个选项之一:

  1. (Least general) Use as_ref() on the Box when you call do_something . (最不通用)调用do_something时在Box上使用as_ref()
fn do_something<T: MyTrait + ?Sized>(obj: &T) {
    obj.foo();
}

fn main() {
    let boxed_value = create_object(MyEnum::A);
    do_something(boxed_value.as_ref());
}
  1. (Most general) Replace the type of obj in do_something with impl AsRef<T> . (最一般)用impl AsRef<T>替换do_something中的obj类型。 This will make do_something work with anything convertible to a &T .这将使do_something与任何可转换为&T的东西一起工作。
fn do_something<T: MyTrait + ?Sized>(obj: impl AsRef<T>) {
    obj.as_ref().foo();
}

fn main() {
    let boxed_value = create_object(MyEnum::A);
    do_something(boxed_value);
}
  1. (Medium general) Replace the type of obj in do_something with impl Deref<Target=T> . (中一般)将do_something中的obj类型替换为impl Deref<Target=T> This will make do_something work with any smart pointer holding a T (which is a bit more restrictive than AsRef<T> — a type can implement AsRef<T> for as many values of T as it wants, but only gets to have one Deref implementation).这将使do_something可以与任何持有T的智能指针一起工作(这比AsRef<T>限制性更强——一个类型可以实现AsRef<T>为它想要的任意数量的T值,但只能有一个Deref执行)。
use std::ops::Deref;

fn do_something<T: MyTrait + ?Sized>(obj: impl Deref<Target=T>) {
    obj.deref().foo();
}

fn main() {
    let boxed_value = create_object(MyEnum::A);
    do_something(boxed_value);
}

Instead of trying to unbox the value you can instead implement MyTrait on Box<dyn MyTrait> and forward to the boxed value.您可以在Box<dyn MyTrait>上实现MyTrait并转发到已装箱的值,而不是尝试拆箱该值。

impl MyTrait for Box<dyn MyTrait> {
    fn foo(&self) {
        self.deref().foo()
    }
}

Then you don't even need to call borrow_mut .然后你甚至不需要调用borrow_mut

fn main() {
    use std::borrow::BorrowMut;
    let boxed_value = create_object(MyEnum::A);
    do_something(&boxed_value);
}

There's a working example in the playground 操场上有一个工作示例

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

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