闭包中的可变性问题

Pow*_*rce 3 closures mutable rust

我真的不知道如何克服这个问题.据我所知,它words被移入封闭(这对我来说很好,它是唯一可以在此之后使用的地方),但需要根据而来typed_some.错误提示听起来像一个体面的想法,只是该部分在一个库中,我不知道它是否是他们可以实现的东西.
on_edit文档.

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
    }
}
Run Code Online (Sandbox Code Playgroud)

而错误

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))
   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Run Code Online (Sandbox Code Playgroud)

回购链接,如果你宁愿克隆它,一切都被推动.承诺633ed60具体.

use*_*342 6

on_edit实际上需要一个不可变的回调.这是否是开发人员的疏忽或有意识的决定并不明显,但是您的代码必须通过使闭包只能不可访问地访问其封闭环境来尊重它.

Rust确实为这种情况提供了逃生舱:RefCell类型.而不是移动WordBar到闭包中,移动a RefCell<WordBar>,然后使用其borrow_mut()方法可变地借用,将借用检查移动到运行时.这编译:

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();
}
Run Code Online (Sandbox Code Playgroud)

请注意,尽管绕过了编译时借用检查,上面的代码并没有放弃安全代码的保证,它只是将检查移动到运行时.RefCell不会允许已借入的单元再次借用 - 如果该值已经借入,borrow_mut()则会发出恐慌声.

由您的代码来确保不会触发此恐慌 - 在这种情况下,通过确保传递的闭包执行的on_edit操作不会导致在闭包返回之前on_edit对其进行调用EditView.