闭包中的可变性问题 [英] Problems with mutability in a closure
问题描述
我真的不知道该如何克服.据我了解,words
被移到了闭包中(对我来说很好,这是它之后唯一要使用的地方),但是根据typed_some
,它必须是& mut.错误提示听起来像是一个不错的主意,只是该部分位于库中,我不知道它们是否可以实现.
on_edit
文档.
I really don't know how to get past this. As far as I understand it, words
is moved into the closure (which is fine by me, it's the only place it's going to be used after this) but needs to be &mut according to typed_some
. What the error suggests sounds like a decent idea, it's just that that part is in a library and I don't know if it'd be something they could implement.
on_edit
documentation.
extern crate cursive;
extern crate rand;
use cursive::Cursive;
use cursive::views::{Dialog, TextView, EditView, LinearLayout};
use cursive::traits::Identifiable;
use rand::Rng;
fn main() {
// This really messes with stdout. Seems to disable it by default but when
// siv is running println prints in random places on the screen.
let mut siv = Cursive::new();
siv.add_global_callback('q', |s| s.quit());
let mut words = WordBar::new();
siv.add_layer(Dialog::around(LinearLayout::vertical()
.child(TextView::new(words.update_and_get_bar()).with_id("target_field"))
.child(EditView::new()
.on_edit(move |s, input, _| words.typed_some(s, input))
.with_id("input_field")))
.title("Keyurses")
.button("Quit", |s| s.quit()));
siv.run();
}
type WordList = Vec<&'static str>;
#[derive(Debug)]
struct WordBar {
words: WordList,
target_list: WordList,
}
impl WordBar {
fn new() -> Self {
WordBar {
words: include_str!("google-10000-english-usa.txt").lines().collect(),
target_list: vec!["foo"],
}
}
fn typed_some(&mut self, siv: &mut Cursive, input: &str) {
// See https://github.com/gyscos/Cursive/issues/102
// for discussion on this mess
let mut reset_input = false;
{
let target_word = siv.find_id::<TextView>("target_field").unwrap();
if target_word.get_content() == input {
target_word.set_content(self.update_and_get_bar());
reset_input = true;
}
}
if reset_input {
siv.find_id::<EditView>("input_field").unwrap().set_content("");
}
}
fn rand_word(&self) -> &'static str {
let mut rng = rand::thread_rng();
rng.choose(&self.words).unwrap()
}
fn update_and_get_bar(&mut self) -> String {
if self.target_list.len() > 0 {
self.target_list.pop();
}
while self.target_list.len() < 5 {
let new_word = self.rand_word();
self.target_list.push(new_word);
}
let mut bar_text: String = "".to_string();
for word in &self.target_list {
if bar_text == "" {
bar_text = word.to_string();
} else {
bar_text.push_str(" ");
bar_text.push_str(word);
}
}
bar_text
}
}
还有错误
error: cannot borrow captured outer variable in an `Fn` closure as mutable
--> src/main.rs:20:45
|
20 | .on_edit(move |s, input, _| words.typed_some(s, input))
| ^^^^^
|
help: consider changing this closure to take self by mutable reference
--> src/main.rs:20:26
|
20 | .on_edit(move |s, input, _| words.typed_some(s, input))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
回购链接如果您希望克隆它,一切都会顺利进行.明确指定633ed60.
Repo link if you'd rather clone it, everything's pushed. Commit 633ed60 to be specific.
推荐答案
on_edit
实际上需要一个不变的回调.这是开发人员的疏忽还是有意识的决定,这一点并不明显,但是您的代码必须通过让闭包不可变地访问其封闭环境来尊重它.
on_edit
actually requires an immutable callback. It is not obvious whether that is an oversight or a conscious decision by developers, but your code must respect it by having the closure only access its enclosing environment immutably.
Rust does provide an escape hatch for such situations: the RefCell
type. Instead of moving the WordBar
into the closure, move a RefCell<WordBar>
, and then use its borrow_mut()
method to borrow mutably, moving the borrow check to run-time. This compiles:
fn main() {
let mut siv = Cursive::new();
siv.add_global_callback('q', |s| s.quit());
let words = ::std::cell::RefCell::new(WordBar::new());
let text = words.borrow_mut().update_and_get_bar();
siv.add_layer(Dialog::around(LinearLayout::vertical()
.child(TextView::new(text)
.with_id("target_field"))
.child(EditView::new()
.on_edit(move |s, input, _|
words.borrow_mut().typed_some(s, input))
.with_id("input_field")))
.title("Keyurses")
.button("Quit", |s| s.quit()));
siv.run();
}
请注意,尽管绕过了编译时借用检查,但是上面的代码并没有放弃对安全代码的保证,它只是将检查移到了运行时. RefCell
不允许再次借用已经借用的单元格-如果值已经借用,则对borrow_mut()
的调用会出现紧急情况.
Note that despite bypassing the compile-time borrow check, the above code doesn't give up the guarantees of safe code, it just moves the check into the run-time. RefCell
will not allow an already borrowed cell to be borrowed again - if the value is already borrowed, a call to borrow_mut()
will panic.
由您的代码确定不会触发此紧急情况-在这种情况下,请确保由传递给on_edit
的闭包执行的操作不会导致在同一EditView
直到关闭返回.
It is up to your code to ensure that this panic is not triggered - in this case by making sure that the actions performed by the closure passed to on_edit
don't cause on_edit
to be invoked on the same EditView
until the closure returns.
这篇关于闭包中的可变性问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!