sin*_*nsv 3 closures callback rust
我有一些结构,并希望该结构的方法作为回调。我尝试将回调设置为闭包并调用其中的方法,但这不起作用。
这是我想要的示例。在此示例中,我使用Cursive库:
extern crate cursive;
use cursive::Cursive;
use cursive::views::{BoxView, SelectView, IdView};
use cursive::view::Selector;
struct SomeStruct {
siv: Cursive,
}
impl SomeStruct {
fn new(siv: Cursive) -> SomeStruct {
let mut ss = SomeStruct {
siv: siv
};
let mut select: SelectView<i32> = SelectView::new();
select.set_on_submit(|siv, value| ss.on_submit_callback(siv, value));
ss.siv.add_fullscreen_layer(BoxView::with_full_screen(IdView::new("select", select)));
ss
}
fn on_submit_callback(&mut self, siv: &mut Cursive, value: &i32) {
println!("value - {}", value);
}
}
fn main() {
let siv = Cursive::new();
let mut ss = SomeStruct::new(siv);
}
Run Code Online (Sandbox Code Playgroud)
有编译器错误:
error[E0373]: closure may outlive the current function, but it borrows `ss`, which is owned by the current function
--> src/main.rs:20:34
|
20 | select.set_on_submit(|siv, value| ss.on_submit_callback(siv, value));
| ^^^^^^^^^^^^ -- `ss` is borrowed here
| |
| may outlive borrowed value `ss`
|
help: to force the closure to take ownership of `ss` (and any other referenced variables), use the `move` keyword, as shown:
| select.set_on_submit(move |siv, value| ss.on_submit_callback(siv, value));
error[E0387]: cannot borrow data mutably in a captured outer variable in an `Fn` closure
--> src/main.rs:20:47
|
20 | select.set_on_submit(|siv, value| ss.on_submit_callback(siv, value));
| ^^
|
help: consider changing this closure to take self by mutable reference
--> src/main.rs:20:34
|
20 | select.set_on_submit(|siv, value| ss.on_submit_callback(siv, value));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 2 previous errors
Run Code Online (Sandbox Code Playgroud)
老实说,Rust 对回调不太友好。
回调在垃圾收集语言中很容易,因为共享对象是一个问题,但在所有权明确的语言中往往更困难。在这种情况下,您有两种可能性:
后者对于小型应用程序来说足够简单,但在大规模应用程序中可能会变得非常混乱(并且存在泄漏)。不过,让我们从它开始吧。
Python 最直接的翻译是使用共享所有权:引用计数指针Rc,包装一些Cellor RefCell(将借用检查推迟到运行时)。
fn new(siv: Cursive) -> Rc<RefCell<SomeStruct>> {
let mut ss = Rc::new(RefCell::new(SomeStruct {
siv: siv
}));
let mut select: SelectView<i32> =
Rc::new(RefCell::new(SelectView::new()));
{
let weak = ss.clone().downgrade();
select.borrow_mut().set_on_submit(|siv, value|
weak.upgrade()
.map(|ss| ss.borrow_mut().on_submit_callback(siv, value))
);
}
ss.borrow_mut().siv.add_fullscreen_layer(
BoxView::with_full_screen(IdView::new("select", select))
);
ss
}
Run Code Online (Sandbox Code Playgroud)
那么,让我们弄清楚我们的所有权故事:
ss拥有(部分)select,select对 的引用较弱ss。弱引用对于打破循环是必要的,因为这会泄漏。
该解决方案应该有效,但如上所述:
Weak打破循环有点复杂,RefCell如果您尝试同时借用两次,其中一次借用是可变的,您将得到一个panic.简而言之:它有效,但不太好。
另一种解决方案是使用事件管理器。
事件循环是解耦所有权的简单解决方案:不是直接调用on_submit_callback回调,而是将事件推送到事件管理器!
解决方案有点复杂:
SomeStruct(可能还需要其他东西),另一方面,它工作得很好,并且解耦的系统可以更容易交互。