我刚开始学习Rust。为此我在Rust中重写我的C ++项目,但最大的问题是闭包的生命周期等。
我创建了here及以下问题的绝对最小问题:
use std::sync::Arc;
use std::cell::{RefCell, Cell};
struct Context {
handler: RefCell<Option<Arc<Handler>>>,
}
impl Context {
pub fn new() -> Arc<Context> {
let context = Arc::new(Context{
handler: RefCell::new(None),
});
let handler = Handler::new(context.clone());
(*context.handler.borrow_mut()) = Some(handler);
context
}
pub fn get_handler(&self) -> Arc<Handler> {
self.handler.borrow().as_ref().unwrap().clone()
}
}
struct Handler {
context: Arc<Context>,
clickables: RefCell<Vec<Arc<Clickable>>>,
}
impl Handler {
pub fn new(context: Arc<Context>) -> Arc<Handler> {
Arc::new(Handler{
context: context,
clickables: RefCell::new(Vec::new()),
})
}
pub fn add_clickable(&self, clickable: Arc<Clickable>) {
self.clickables.borrow_mut().push(clickable);
}
pub fn remove_clickable(&self, clickable: Arc<Clickable>) {
// remove stuff ...
}
}
struct Clickable {
context: Arc<Context>,
callback: RefCell<Option<Box<Fn()>>>,
}
impl Clickable {
pub fn new(context: Arc<Context>) -> Arc<Clickable> {
let clickable = Arc::new(Clickable{
context: context.clone(),
callback: RefCell::new(None),
});
context.get_handler().add_clickable(clickable.clone());
clickable
}
pub fn remove(clickable: Arc<Clickable>) {
clickable.context.get_handler().remove_clickable(clickable);
}
pub fn set_callback(&self, callback: Option<Box<Fn()>>) {
(*self.callback.borrow_mut()) = callback;
}
pub fn click(&self) {
match *self.callback.borrow() {
Some(ref callback) => (callback)(),
None => (),
}
}
}
struct Button {
context: Arc<Context>,
clickable: Arc<Clickable>,
}
impl Button {
pub fn new(context: Arc<Context>) -> Arc<Button> {
let clickable = Clickable::new(context.clone());
let button = Arc::new(Button{
context: context,
clickable: clickable.clone(),
});
let tmp_callback = Box::new(|| {
button.do_stuff();
});
clickable.set_callback(Some(tmp_callback));
button
}
pub fn do_stuff(&self) {
// doing crazy stuff
let mut i = 0;
for j in 0..100 {
i = j*i;
}
}
pub fn click(&self) {
self.clickable.click();
}
}
impl Drop for Button {
fn drop(&mut self) {
Clickable::remove(self.clickable.clone());
}
}
fn main() {
let context = Context::new();
let button = Button::new(context.clone());
button.click();
}
我只是不知道如何在闭包中传递引用。
另一个丑陋的事情是我的Handler
和我的Context
需要彼此。有没有更好的方法来创建这种依赖?
答案 0 :(得分:2)
取消初始代码
pub fn new(context: Arc<Context>) -> Arc<Button> {
let clickable = Clickable::new(context.clone());
let button = Arc::new(Button{
context: context,
clickable: clickable.clone(),
});
let tmp_callback = Box::new(|| {
button.do_stuff();
});
clickable.set_callback(Some(tmp_callback));
button
}
首先,请注意您的错误
error[E0373]: closure may outlive the current function, but it borrows `button`, which is owned by the current function
--> src/main.rs:101:37
|
101 | let tmp_callback = Box::new(|| {
| ^^ may outlive borrowed value `button`
102 | button.do_stuff();
| ------ `button` is borrowed here
|
help: to force the closure to take ownership of `button` (and any other referenced variables), use the `move` keyword, as shown:
| let tmp_callback = Box::new(move || {
注意到底部的help
块,您需要使用move
闭包,因为当new
函数结束时,堆栈上的button
变量将会出现超出范围。避免这种情况的唯一方法是将其所有权移至回调本身。因此,您需要更改
let tmp_callback = Box::new(|| {
到
let tmp_callback = Box::new(move || {
现在,你得到第二个错误:
error[E0382]: use of moved value: `button`
--> src/main.rs:107:9
|
102 | let tmp_callback = Box::new(move || {
| ------- value moved (into closure) here
...
107 | button
| ^^^^^^ value used here after move
|
= note: move occurs because `button` has type `std::sync::Arc<Button>`, which does not implement the `Copy` trait
这里的错误可能会更清楚一些。您尝试将button
值的所有权移至回调闭包中,但当您返回时,还在new
函数体内使用它,并且你不能试图拥有这两个不同的东西。
对此的解决方案有望成为你的猜测。您必须制作一份可以获得所有权的副本。你想要改变
let tmp_callback = Box::new(move || {
button.do_stuff();
到
let button_clone = button.clone();
let tmp_callback = Box::new(move || {
button_clone.do_stuff();
现在您已经创建了一个新的Button
对象,并为该对象本身返回了Arc
,同时还为回调本身提供了第二个Arc
的所有权。< / p>
鉴于您的评论,这里确实存在循环依赖的问题,因为您的Clickable
对象拥有对Button
的引用的所有权,而Button
拥有对{的引用的所有权{1}}。解决这个问题的最简单方法是从
Clickable
到
let button_clone = button.clone();
let tmp_callback = Box::new(move || {
button_clone.do_stuff();
因此let button_weak = Arc::downgrade(&button);
let tmp_callback = Box::new(move || {
if let Some(button) = button_weak.upgrade() {
button.do_stuff();
}
});
只会保留对Clickable
的弱引用,如果不再引用Button
,则回调将为无操作。
您可能还想考虑将Button
列为clickables
引用而不是强引用,这样您就可以在删除引用的项目时从中删除项目。