rust - 闭包中的可变性问题

标签 rust closures mutable

我真的不知道如何克服这个问题。据我了解,words 被移入闭包中(这对我来说很好,这是此后唯一要使用的地方)但需要根据 typed_some 进行 &mut 。错误提示的内容听起来是个不错的主意,只是那个部分在库中,我不知道他们是否可以实现它。
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))
   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Repo link如果您想克隆它,一切都会被推送。具体请提交 633ed60。

最佳答案

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

Rust 确实为这种情况提供了一个逃生 channel : RefCell type .而不是移动 WordBar进入闭包,移动一个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();
}

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

由您的代码来确保不会触发此 panic - 在这种情况下,通过确保闭包执行的操作传递给 on_edit不要造成on_edit在相同的 EditView 上调用直到闭包返回。

关于rust - 闭包中的可变性问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41990175/

相关文章:

python - 列表列表更改意外地反射(reflect)在子列表中

rust - error[E0554] : #! [feature] 可能无法在稳定发布 channel 上使用无法使用 cargo 安装赛车

rust - 为什么在通过 massif 执行时连接到 Rust 中的 MySQL 会崩溃?

arrays - 具有多个闭包的通用函数不起作用

c++ - C++11 lambda 的参数/存储类型

python - 如何测试可变的隐含结果?

rust - Rust 引用书中的问题

使用数组参数从 C 调用 Rust 方法

swift - 如何在 Swift 中使用带有返回的 completionHandler Closure?

closures - 将 &mut 传递给函数并返回闭包的生命周期问题