rust - 如何在 Rust 中按值传递装箱的 trait 对象?

标签 rust traits trait-objects

我正在编写一些代码,并且有一个使用 self 的方法的特征。按值(value)。我想在 Box 上调用这个方法'd trait 对象(消耗 Box 及其值)。这可能吗?如果是这样,如何?
在代码方面,一个最小的例子看起来像以下(不完整的)代码:

trait Consumable {
    fn consume(self) -> u64;
}
fn consume_box(ptr: Box<dyn Consumable>) -> u64 {
    //what can I put here?
}
我的问题是如何填写函数consume_box具有指定的签名,以便返回的值是通过调用 consume 获得的任何值。在 Box 'd 值。
我最初写的
ptr.consume()
作为函数的主体,虽然我意识到这不是一个正确的想法,因为它没有理解我想要 Box 的事实。被消费,不仅仅是它的内容,但它是我唯一能想到的东西。这不会编译,给出一个错误:

cannot move a value of type dyn Consumable: the size of dyn Consumable cannot be statically determined


这对我来说有点令人惊讶,因为我是 Rust 的新手,我认为可能是 self参数的传递类似于 C++ 中的右值引用(这正是我想要的 - 在 C++ 中,我可能会通过带有签名 virtual std::uint64_t consume() && 的方法来实现这一点,让 std::unique_ptr 通过虚拟析构函数),但我猜 Rust 确实是按值传递,将参数移动到位之前 - 所以它拒绝代码是合理的。
问题是,我不知道如何获得我想要的行为,在那里我可以使用 Box 'd trait 对象。我尝试使用默认实现向 trait 添加一个方法,认为这可能会让我在 vtable 中得到一些有用的东西:
trait Consumable {
    fn consume(self) -> u64;
    fn consume_box(me: Box<Self>) -> u64 {
        me.consume()
    }
}
但是,这会产生错误

the trait Consumable cannot be made into an object


当我提到 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 这样直接代表一棵树的事情) - 因此,为什么我要问按值传递而不是尝试重写它以按引用传递。

最佳答案

目前不支持此功能。一个 dyn Consumable表示一个非大小的类型,除了通过间接(通过引用或 Box -like structs)之外非常有限。
然而有RFC 1909: Unsized RValues希望放宽其中一些限制。一种能够传递未定义大小的函数参数,例如 self在这种情况下。当每晚使用 unsized_fn_params 编译时,此 RFC 的当前实现接受您的初始代码。 :

#![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());
}
playground .

关于rust - 如何在 Rust 中按值传递装箱的 trait 对象?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65261399/

相关文章:

rust - dyn MyTrait的大小无法在采用self的方法中静态确定。

rust - 如何结合 `Ord::cmp()`中的两个cmp条件

rust - 编译时检查编译器是否nightly

rust - 文件结构不被视为实现读取?

c++ - 用于将静态字符串常量与类型相关联的类型特征模式

types - 为什么由于缺少类型注释而收到错误 "trait bound FromStr is not satisfied"?

rust - 哪些 Rust 1.2 容器支持特征对象?

rust - 如何将结构的 Vec 强制转换为特征对象的 Vec?

rust - 什么使某些东西成为 "trait object"?

rust - 如何移动拥有的指针