我有一个案例,我的一个结构体
Grid
具有 Vec
特征 Listener
实现。当 Grid
首次创建时,此列表将填充 Relay
实例。
A
Relay
是一个 Listener
,它将传入信号传播到队列中的下一个 Listener
。 Relay
被链接起来,以便每个继电器都知道队列中的下一个继电器(参见 Grid::new
)。
但是,
Grid
的所有者想要创建自己的Listener
,以便在中继链的末端被调用。因此,Grid::new
接受 Listener
用作最后一个 Relay
中的“下一个”侦听器。
由于继电器全部归
Grid
所有,但还需要由另一个 Relay
可变地引用,因此我使用 Rc<RefCell<dyn Listener>>
来管理双重所有权。
use rand::{thread_rng, Rng};
use std::cell::RefCell;
use std::rc::Rc;
trait Listener {
fn on_signal(&mut self);
}
struct Relay {
next: Rc<RefCell<dyn Listener>>,
}
impl Listener for Relay {
fn on_signal(&mut self) {
self.next.borrow_mut().on_signal();
}
}
struct Receiver {}
impl Listener for Receiver {
fn on_signal(&mut self) {
println!("Signal Received!");
}
}
struct Grid {
relays: Vec<Rc<RefCell<Relay>>>,
}
impl Grid {
fn new(listener: impl Listener) -> Self {
Self {
relays: (0..10).fold(Vec::new(), |mut acc, _| {
acc.push(Rc::new(RefCell::new(Relay {
next: match acc.last() {
None => Rc::new(RefCell::new(listener)),
Some(listener) => listener.clone(),
},
})));
acc
}),
}
}
fn transmit(&mut self) {
self.relays[thread_rng().gen_range(0..self.relays.len())]
.borrow_mut()
.on_signal();
}
}
fn main() {
Grid::new(Receiver {}).transmit();
// But also
Grid::new(Relay {
next: Rc::new(RefCell::new(Receiver {})),
})
.transmit();
}
但是,我无法让它发挥作用。首先我得到
the parameter type `impl Listener` may not live long enough [E0310]
所以基于this答案,我尝试这样做
- fn new(listener: impl Listener) -> Self {
+ fn new(listener: impl Listener + 'static) -> Self {
但这给了我
cannot move out of `listener`, a captured variable in an `FnMut` closure [E0507]
我不知道如何让它发挥作用。我对
impl
与 dyn
以及 fn
与 Fn
与 FnMut
感到困惑。很多人建议Box<dyn Listener>
,但我正在使用Rc<RefCell<dyn Listener>>
,所以我认为这里不需要拳击?此外,要求 Grid::new
的调用者对其实现进行装箱似乎相当尴尬。
我怎样才能让我的
Grid::new
方法发挥作用?
FnMut
是一个可以多次调用的闭包,并且可以在其捕获中保存独占引用。
现在你的关闭
|mut acc, _| {
acc.push(Rc::new(RefCell::new(Relay {
next: match acc.last() {
None => Rc::new(RefCell::new(listener)),
Some(listener) => listener.clone(),
},
})));
acc
}
在它的
None
路径中,consumes listener
,不幸的是编译器看起来不够深入,无法看到这条路径只被采用一次。幸运的是,要让编译器相信,如果不再有值,代码就会发散,这并不难,只需将 listener
包装在 Option
中,我们就可以 Option::take
和 unwrap
,现在,如果不再有值,则代码会出现恐慌,编译器可以看到这是可以的:
impl Grid {
fn new(listener: impl Listener) -> Self {
let mut listener = Some(listener);
Self {
relays: (0..10).fold(Vec::new(), |mut acc, _| {
acc.push(Rc::new(RefCell::new(Relay {
next: match acc.last() {
None => Rc::new(RefCell::new(listener.take().unwrap())),
Some(listener) => listener.clone(),
},
})));
acc
}),
}
}
}