我如何接受我的结构的通用特征实现并传播它?

问题描述 投票:0回答:1

我有一个案例,我的一个结构体

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
方法发挥作用?

generics rust
1个回答
0
投票

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
            }),
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.