我有一个实现的草图:
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
是一个特征,我存储Vec<Box<Listener>>
。
我很难为commit
实施Transaction
。不知何故,我必须通过在每个存储的commit
上调用Listener
来使用这些框,但据我所知,我无法将这些内容移出框中。
我如何在提交时使用我的侦听器?
答案 0 :(得分:8)
不允许将commit
应用于盒装对象,因为特征对象不知道它的大小(并且它在编译时不是常量)。由于您计划将侦听器用作盒装对象,因此您可以确认将在框中调用commit
并相应地更改其签名:
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
的大小是众所周知的,并且完全有可能将对象移出盒子并消耗两者。
此解决方案的价格是Listener::commit
现在需要一个Box
。如果这是不可接受的,您可以在特征中声明commit(self)
和commit_boxed(self: Box<Self>)
,要求所有类型都实现这两种类型,可能使用私有函数或宏来避免代码重复。这不是很优雅,但它可以同时满足盒装和无盒装用例而不会降低性能。
答案 1 :(得分:1)
启用unsized_locals
功能后,自然代码works as-is:
// 1.37.0-nightly 2019-06-03 6ffb8f53ee1cb0903f9d
#![feature(unsized_locals)]
// ...
impl Transaction {
fn commit(self) {
for l in self.listeners {
l.commit()
}
}
}