[英]How to pass a boxed trait object by value in Rust?
我正在编写一些代码,并且有一个特点,即一种按价值获取self
的方法。 我想在Box
的特征 object 上调用此方法(使用Box
及其值)。 这可能吗? 如果是这样,如何?
在代码方面,最小示例类似于以下(不完整)代码:
trait Consumable {
fn consume(self) -> u64;
}
fn consume_box(ptr: Box<dyn Consumable>) -> u64 {
//what can I put here?
}
我的问题是如何使用指定的签名填充consume_box
,以便返回的值是通过调用consume
对Box
的值获得的任何值。
我最初写过
ptr.consume()
作为 function 的主体,虽然我意识到这不是完全正确的想法,因为它没有理解我希望Box
被消耗的事实,而不仅仅是它的内容,但这是我唯一能想到的的。 这不会编译,报错:
无法移动类型为 dyn Consumable 的值:无法静态确定 dyn Consumable 的大小
这让我有些惊讶,作为 Rust 的新手,我曾认为可能self
参数的传递类似于 C++ 中的右值引用(这正是我想要的 - 在 C++ 中,我可能会通过一个方法来实现它signature virtual std::uint64_t consume() &&
,让std::unique_ptr
通过虚拟析构函数清理移出的 object ),但我猜 Rust 确实是按值传递,将参数移动到位之前 - 所以这是合理的它拒绝代码。
问题是,我不确定如何获得我想要的行为,在那里我可以使用Box
的特征 object。我尝试使用默认实现向特征添加一个方法,认为这可能会让我在 vtable 中得到一些有用的东西:
trait Consumable {
fn consume(self) -> u64;
fn consume_box(me: Box<Self>) -> u64 {
me.consume()
}
}
但是,这会产生错误
特性
Consumable
无法制成 object
当我提到Box<dyn Consumable>
类型时——这并不奇怪,因为编译器弄清楚如何处理参数类型随Self
变化的 function 本来就是奇迹。
是否可以使用提供的签名实现consume_box
- 甚至在必要时修改特征?
如果它有用,更具体地说,这是某些数学表达式的一种表示形式的一部分——也许玩具 model 是具体的实现,大致如下所示:
impl Consumable for u64 {
fn consume(self) -> u64 {
self
}
}
struct Sum<A, B>(A, B);
impl<A: Consumable, B: Consumable> Consumable for Sum<A, B> {
fn consume(self) -> u64 {
self.0.consume() + self.1.consume()
}
}
struct Product<A, B>(A, B);
impl<A: Consumable, B: Consumable> Consumable for Product<A, B> {
fn consume(self) -> u64 {
self.0.consume() * self.1.consume()
}
}
fn parse(&str) -> Option<Box<dyn Consumable> > {
//do fancy stuff
}
其中,在大多数情况下,事物是普通的旧数据(但由于泛型,它可能是任意大的块),但也让它与传递更多不透明的句柄到这些类型的事物兼容 - 因此希望能够与Box<dyn Consumable>
一起工作。 至少在语言层面,这是一个很好的 model 我在做什么——这些对象拥有的唯一资源是 memory 的片段(与多线程无关,也没有自引用的恶作剧)——尽管这model 没有捕捉到我所拥有的用例是一个对实现使用 object 而不是仅仅读取它有用的用例,它也不恰当 model 我想要一个“开放的” class 可能的段而不是有限集可能性(使得很难像直接表示树的enum
那样做) - 因此我为什么要问按值传递而不是试图重写它以按引用传递。
目前不支持此功能。 一个dyn Consumable
表示一个大小不一的类型,除了通过间接方式(通过引用或类似Box
的结构)外,它非常有限。
但是有RFC 1909: Unsized RValues希望放宽这些限制。 一个能够传递未调整大小的 function 参数,例如本例中的self
。 此 RFC 的当前实现在使用unsized_fn_params
每晚编译时接受您的初始代码:
#![feature(unsized_fn_params)]
trait Consumable {
fn consume(self) -> u64;
}
struct Foo;
impl Consumable for Foo {
fn consume(self) -> u64 {
42
}
}
fn main () {
let ptr: Box<dyn Consumable> = Box::new(Foo);
println!("result is {}", ptr.consume());
}
在操场上见。
我相信
trait Consumable {
fn consume(self) -> u64;
}
fn consume_box(val: impl Consumable) -> u64 {
val.consume()
}
可能会做你想做的事。 I'm all but a Rust expert - or C++ expert for that matter -, but I think it should be working pretty much like the move-semantics in C++ you mentioned in terms of memory behavior. 据我了解,这是一种通用形式,其中 Rust 为您调用的每种类型实现 Function 。
如果不用nightly Rust,我这里写了个宏。 它自动生成第二个特征 function。
trait Consumable {
fn consume(self) -> u64;
fn consume_box(me: Box<Self>) -> u64 ;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.