[英]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++ 中发生的情况,当您传递对基类的引用时),但这似乎不是案子。
您可以通过强制转换(或最终仅键入归属)并放宽T
为Sized
的默认要求来实现这一点:
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
中的特征绑定,然后我认为您有以下三个选项之一:
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());
}
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);
}
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.