Wor*_*der 10 ownership-semantics rust
我有一个实现的草图:
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();
}
Run Code Online (Sandbox Code Playgroud)
我可以Transaction在它们上面使用侦听器,当事务发生时会调用侦听器.既然Listener是特质,我会存储一个Vec<Box<Listener>>.
我有一个很难实现commit的Transaction.不知怎的,我必须通过调用commit每个存储的Listeners 来消耗这些盒子,但据我所知,我无法从盒子中移出东西.
如何在提交时使用我的监听器?
use*_*342 12
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");
}
}
Run Code Online (Sandbox Code Playgroud)
这样可以Transaction在编写时进行编译,因为在FooListener大小的实现中Self是众所周知的,并且完全可以将对象移出框中并同时使用它们.
这个解决方案的价格Listener::commit现在需要一个Box.如果这是不可接受的,您可以在特征中声明两者,commit(self)并commit_boxed(self: Box<Self>)要求所有类型都实现两者,可能使用私有函数或宏来避免代码重复.这不是很优雅,但它可以满足盒装和无盒装用例而不会降低性能.
接受的答案显示了当您有权修改原始Listener特征时该怎么做。如果您没有该选项,即如果您控制Transaction类型,但不能控制Listener其实现,请继续阅读。
首先,我们创建一个对象安全的辅助特征,因为它的方法都不消耗self:
trait DynListener {
fn some_action(&mut self);
fn commit(&mut self);
}
Run Code Online (Sandbox Code Playgroud)
为了在任何地方都可以使用这个特征Listener,我们将提供该特征的全面实现。通常这样的实现将为DynListener所有类型实现T: Listener。但这在这里不起作用,因为Listener::commit()需要 消耗self,并且DynListener::commit()只接收引用,因此调用Listener::commit()将无法编译,并显示“无法移出借用的内容”。为了解决这个问题,我们实施了DynListenerfor Option<T>。这允许我们用来获取Option::take()要传递给的自有值Listener::commit():
impl<T: Listener> DynListener for Option<T> {
fn some_action(&mut self) {
// self is &mut Option<T>, self.as_mut().unwrap() is &mut T
self.as_mut().unwrap().some_action();
}
fn commit(&mut self) {
// self is &mut Option<T>, self.take().unwrap() is T
self.take().unwrap().commit();
}
}
Run Code Online (Sandbox Code Playgroud)
DynListener::commit()从 中取出值Option,调用Listener::commit()该值,并将选项保留为None。T之所以能够编译,是因为在已知每个个体大小的总体实现中,该值并不是“未确定大小”的。缺点是我们可以对DynListener::commit()同一个选项多次调用,除了第一次之外的所有尝试都会在运行时发生恐慌。
剩下的工作是修改Transaction以利用它:
struct Transaction {
listeners: Vec<Box<dyn DynListener>>,
}
impl Transaction {
fn commit(self) {
for mut listener in self.listeners {
listener.commit();
}
}
}
fn listener() {
let transaction = Transaction {
listeners: vec![Box::new(Some(FooListener {}))],
};
transaction.commit();
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1330 次 |
| 最近记录: |