2017-10-07 28 views
3

我有一个实施的下列草图:如何调用在盒装特质对象上消耗自我的方法?

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 s的对他们的听众,将调用监听器当事情发生在该交易。由于Listener是一个特征,我存储Vec<Box<Listener>>

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

我会如何消费我的听众提交?

+0

将“东西”移出盒子很容易; [你只需解除引用](https://stackoverflow.com/questions/42264041/how-do-i-get-an-owned-value-out-of-a-box)。你的情况更复杂,因为你不再知道存储箱内存储的值有多大。这意味着你会收到一个错误:*无法移动类型监听器的值:监听器的大小不能静态确定*。 – Shepmaster

回答

3

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现在要求 a Box。如果这是不可接受的,则可以在特征中声明commit(self)commit_boxed(self: Box<Self>),要求所有类型都实现两者,可能使用私有函数或宏以避免代码重复。这不是非常优雅,但它可以同时满足盒装和非盒装用例,而不会损失性能。

相关问题