我正在编写一些代码,并且具有一种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
用指定的签名填充函数,以便返回的值是通过调用'd 值获得的任何值consume
。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>
类型时——这并不奇怪,因为编译器弄清楚如何处理参数类型不同的函数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
直接代表一棵树) - 因此我问的是按值传递而不是尝试重写它以通过引用传递。