繁体   English   中英

如何在 Rust 中按值传递盒装特征 object?

[英]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 ,以便返回的值是通过调用consumeBox的值获得的任何值。

我最初写过

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM