我正在编写一些代码,并且有一个方法的特征,该方法需要self
按价值。我想调用这个方法Box
'd 特征对象(消耗Box
及其价值)。这可能吗?如果是这样,怎么办?
就代码而言,最小示例如下(不完整)代码:
trait Consumable {
fn consume(self) -> u64;
}
fn consume_box(ptr: Box<dyn Consumable>) -> u64 {
//what can I put here?
}
我的问题是如何填写函数consume_box
具有指定的签名,以便返回的值是通过调用获得的任何值consume
on the Box
很值。
我最初写的是
ptr.consume()
作为函数的主体,尽管我意识到这不是一个正确的想法,因为它没有说明我想要的事实Box
被消耗的不仅仅是它的内容,而是我唯一能想到的东西。这不会编译,给出错误:
无法移动 dyn Consumable 类型的值:无法静态确定 dyn Consumable 的大小
这对我来说有点令人惊讶,作为 Rust 的新手,我原以为也许self
参数的传递方式类似于 C++ 中的右值引用(这确实是我想要的 - 在 C++ 中,我可能会通过带有签名的方法来实现它virtual std::uint64_t consume() &&
,让一个std::unique_ptr
通过虚拟析构函数清理移出的对象),但我猜 Rust 确实是按值传递,将参数移动到之前的位置 - 所以它拒绝代码是合理的。
问题是,我不知道如何获得我想要的行为,我可以在哪里消费Box
d 特质对象。我尝试使用默认实现向特征添加一个方法,认为这可能会在 vtable 中为我带来一些有用的东西:
trait Consumable {
fn consume(self) -> u64;
fn consume_box(me: Box<Self>) -> u64 {
me.consume()
}
}
但是,这会产生错误
特质Consumable
不能被制成物体
当我提到Box<dyn Consumable>
type - 这并不奇怪,因为编译器会计算出如何处理参数类型随参数类型变化的函数Self
那就太神奇了。
是否可以实现该功能consume_box
使用提供的签名 - 甚至在必要时修改特征?
如果它有用,更具体地说,这是某些数学表达式的某种表示的一部分 - 也许玩具模型将是大致如下所示的具体实现:
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>
。至少在语言层面上,这是我要做的事情的一个很好的模型 - 这些对象拥有的唯一资源是内存片段(与多线程无关,也没有自引用恶作剧) - 尽管这个模型没有捕捉到我所拥有的用例对于实现使用对象而不是仅仅读取它是有用的,它也没有适当地建模我想要一个可能段的“开放”类而不是有限集可能性(使得很难做类似的事情enum
直接代表一棵树) - 因此为什么我询问按值传递而不是尝试重写它以按引用传递。