簡體   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. 我想將對盒裝特征 object 的引用傳遞給第二個 function,但我無法弄清楚如何做到這一點。

示例簡化代碼:

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());
}

我得到的錯誤:

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`  

直觀地說,我希望在這種情況下 Rust 將使用動態調度並且不關心具體類型 T(類似於 C++ 中發生的情況,當您傳遞對基類的引用時),但這似乎不是案子。

如何將對盒裝特征 object ( Box<dyn MyTrait> ) 的引用傳遞給第二個 function ( do_something )? 這在某種程度上可能嗎? 需要更改do_something的解決方案也是可以接受的。

直觀地說,我希望在這種情況下 Rust 將使用動態調度並且不關心具體類型 T(類似於 C++ 中發生的情況,當您傳遞對基類的引用時),但這似乎不是案子。

您可以通過強制轉換(或最終僅鍵入歸屬)並放寬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);

但是,如果您不以其他方式使用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());

如果您不關心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());

無論如何,您都需要將?Sized添加到do_something中的特征綁定,然后我認為您有以下三個選項之一:

  1. (最不通用)調用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. (最一般)用impl AsRef<T>替換do_something中的obj類型。 這將使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. (中一般)將do_something中的obj類型替換為impl Deref<Target=T> 這將使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);
}

您可以在Box<dyn MyTrait>上實現MyTrait並轉發到已裝箱的值,而不是嘗試拆箱該值。

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

然后你甚至不需要調用borrow_mut

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

操場上有一個工作示例

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM