I have a case where one of my structs, Grid
has a Vec
of trait Listener
implementations. When Grid
is first created, this list is filled with Relay
instances.
A Relay
is a Listener
which propagates incoming signals to the next Listener
in line. The Relay
s are chained such that each relay knows of the next relay in line (see Grid::new
).
However, the owner of Grid
wants to create their own Listener
to be called at the end of the chain of relays. Grid::new
therefore accepts a Listener
to use as the "next" listener in the last Relay
.
Since the relays are all owned by Grid
, but also need to be referenced mutably by another Relay
, I've used Rc<RefCell<dyn Listener>>
to manage the double ownership.
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();
}
However, I can't get this to work. First I get
the parameter type `impl Listener` may not live long enough [E0310]
so based on this answer, I try to do
- fn new(listener: impl Listener) -> Self {
+ fn new(listener: impl Listener + 'static) -> Self {
but this gives me
cannot move out of `listener`, a captured variable in an `FnMut` closure [E0507]
I'm not sure how to get this to work. I'm confused by impl
vs dyn
as well as fn
vs Fn
vs FnMut
. A lot of people suggest Box<dyn Listener>
but I'm using Rc<RefCell<dyn Listener>>
so I don't think boxing should be necessary here? Also, it seems rather awkward to require callers of Grid::new
to box their implementation.
How can I get my Grid::new
method to work?
FnMut
is a closure that can be called multiple times and can hold exclusive references in its captures.
Now your closure
|mut acc, _| {
acc.push(Rc::new(RefCell::new(Relay {
next: match acc.last() {
None => Rc::new(RefCell::new(listener)),
Some(listener) => listener.clone(),
},
})));
acc
}
in its None
path, consumes listener
. Because unfortunately the compiler can not see that this path is only taken once, it can only be called once. Fortunately it's not hard to convince the compiler that the code diverges if there is no value any more, simply wrap listener
in an Option
that we can Option::take
and unwrap
, now if there is no more value, the code would panic, the compiler can see this is ok:
impl Grid {
fn new(listener: impl Listener + 'static) -> 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
}),
}
}
}