如何在装箱的 trait 对象上调用消耗 self 的方法? [英] How to call a method that consumes self on a boxed trait object?

查看:52
本文介绍了如何在装箱的 trait 对象上调用消耗 self 的方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下实现草图:

trait Listener {
    fn some_action(&mut self);
    fn commit(self);
}

struct FooListener {}

impl Listener for FooListener {
    fn some_action(&mut self) {
        println!("{:?}", "Action!!");
    }

    fn commit(self) {
        println!("{:?}", "Commit");
    }
}

struct Transaction {
    listeners: Vec<Box<Listener>>,
}

impl Transaction {
    fn commit(self) {
        // How would I consume the listeners and call commit() on each of them?
    }
}

fn listener() {
    let transaction = Transaction {
        listeners: vec![Box::new(FooListener {})],
    };
    transaction.commit();
}

我可以让 Transaction 带有侦听器,当交易发生某些事情时,它们会调用侦听器.由于 Listener 是一个 trait,所以我存储了一个 Vec>.

I can have Transactions with listeners on them that will call the listener when something happens on that transaction. Since Listener is a trait, I store a Vec<Box<Listener>>.

我很难为 Transaction 实现 commit.不知何故,我必须通过在每个存储的 Listener 上调用 commit 来消耗这些盒子,但据我所知,我无法将东西移出盒子.

I'm having a hard time implementing commit for Transaction. Somehow I have to consume the boxes by calling commit on each of the stored Listeners, but I can't move stuff out of a box as far as I know.

我如何在提交时消耗我的听众?

How would I consume my listeners on commit?

推荐答案

commit 应用到装箱对象是不允许的,因为 trait 对象不知道它的大小(并且它在编译时不是常量-时间).由于您计划将侦听器用作装箱对象,因此您可以做的是确认将在框上调用 commit 并相应地更改其签名:

Applying commit to the boxed object is not allowed because the trait object doesn't know its size (and it's not constant at compile-time). Since you plan to use listeners as boxed objects, what you can do is acknowledge that commit will be invoked on the box and change its signature accordingly:

trait Listener {
    fn some_action(&mut self);
    fn commit(self: Box<Self>);
}

struct FooListener {}

impl Listener for FooListener {
    fn some_action(&mut self) {
        println!("{:?}", "Action!!");
    }

    fn commit(self: Box<Self>) {
        println!("{:?}", "Commit");
    }
}

这使 Transaction 能够按照您编写的方式进行编译,因为在 FooListener 的实现中,Self 的大小是众所周知的,它是完全有可能将物体从盒子中取出并消耗掉.

This enables Transaction to compile as you wrote it, because inside the implementation of FooListener the size of Self is well known and it is perfectly possible to move the object out of the box and consume both.

这个解决方案的代价是Listener::commit 现在需要一个Box.如果这是不可接受的,您可以在 trait 中同时声明 commit(self)commit_boxed(self: Box),要求所有类型都实现这两者,可能使用私有函数或宏来避免代码重复.这不是很优雅,但它可以在不损失性能的情况下满足装箱和未装箱的用例.

The price of this solution is that Listener::commit now requires a Box. If that is not acceptable, you could declare both commit(self) and commit_boxed(self: Box<Self>) in the trait, requiring all types to implement both, possibly using private functions or macros to avoid code duplication. This is not very elegant, but it would satisfy both the boxed and unboxed use case without loss of performance.

这篇关于如何在装箱的 trait 对象上调用消耗 self 的方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆