Rust:为什么我不能移动一个值两次?

Rust: Why can't I move a value twice?

Rust 的新手,真的觉得我在这里遗漏了一些东西。

在下面的代码中,有两个对问题很重要的嵌套闭包:app.connect_activatedraw_area.connect_draw。我正在尝试使用通道将值发送到 connect_draw 闭包中,以便绘制的内容将反映随时间变化的模拟状态(当前由随机颜色表示)。

我发现的东西对我来说很奇怪,我根本不明白:

换句话说:我可以将创建的通道移动到任一闭包边界,但不能同时移动。它可以从 main 的范围移动到 connect_activate 的范围,或者从 connect_activate 的范围移动到 connect_draw 的范围,但不能按顺序进行。为什么会这样?如果我可以将它移动到 connect_activate,那么它不应该像在那里创建的那样“拥有”吗?这两种情况似乎确实是真正的“移动”,因为如果我省略“移动”关键字它们就会失败。这是怎么回事?

奖金问题:有一个可怕的占位符,我每次收到任何事件时都会重新绘制,因为我仍然不知道如何 运行 关闭计时器的事件。哈尔普?

示例 1:(作品)

extern crate cairo;
extern crate rand;
extern crate gtk;
extern crate gdk;
extern crate glib;
use std::{thread, time};
use gtk::prelude::*;
use gtk::{Application, ApplicationWindow, DrawingArea};
use std::sync::mpsc;
use std::sync::mpsc::{Receiver, Sender};


fn main(){
    let app = Application::builder()
        .application_id("org.example.HelloWorld")
        .build();
    app.connect_activate(move |app| {
        let (tx, rx ) : (Sender<f64>, Receiver<f64>)= mpsc::channel();
        let draw_area = DrawingArea::new();
        let _id = draw_area.connect_draw(move |_unused, f| {
            let red = rx.recv().unwrap();
            let green = rx.recv().unwrap();
            let blue = rx.recv().unwrap();
            f.set_source_rgb(red,green, blue);
            f.paint().expect("Painting failed");
            Inhibit(false)
        });
        let win = ApplicationWindow::builder()
            .application(app)
            .default_width(320)
            .default_height(200)
            .title("Hello, World!")
            .build();
        win.add(&draw_area);

        win.show_all();
        
        win.connect_event(|w, _g|{ //Placeholder until I learn to make queue_draw fire on a timer
            w.queue_draw();
            Inhibit(false)
        });
        thread::spawn(move || {
            loop {
                thread::sleep(time::Duration::from_millis(100));
                tx.send(rand::random::<f64>()).unwrap();
            }
        });
    });
    app.run();
}

示例 2(不起作用)

extern crate cairo;
extern crate rand;
extern crate gtk;
extern crate gdk;
extern crate glib;
use std::{thread, time};
use gtk::prelude::*;
use gtk::{Application, ApplicationWindow, DrawingArea};
use std::sync::mpsc;
use std::sync::mpsc::{Receiver, Sender};


fn main(){
    let app = Application::builder()
        .application_id("org.example.HelloWorld")
        .build();
    let (tx, rx ) : (Sender<f64>, Receiver<f64>)= mpsc::channel();
    app.connect_activate(move |app| {
        let draw_area = DrawingArea::new();
        let _id = draw_area.connect_draw(move |_unused, f| {
            let red = rx.recv().unwrap();
            let green = rx.recv().unwrap();
            let blue = rx.recv().unwrap();
            f.set_source_rgb(red,green, blue);
            f.paint().expect("Painting failed");
            Inhibit(false)
        });
        let win = ApplicationWindow::builder()
            .application(app)
            .default_width(320)
            .default_height(200)
            .title("Hello, World!")
            .build();
        win.add(&draw_area);

        win.show_all();
        
        win.connect_event(|w, _g|{ //Placeholder until I learn to make queue_draw fire on a timer
            w.queue_draw();
            Inhibit(false)
        });
    });
    thread::spawn(move || {
        loop {
            thread::sleep(time::Duration::from_millis(100));
            tx.send(rand::random::<f64>()).unwrap();
        }
    });
    app.run();
}

gtk 应用程序的 connect_activate() 方法接受一个 Fn 参数,这意味着它可以被多次调用。您不能 moveFn 闭包中捕获变量,因为该变量将在您下次尝试调用它时被移动。第一个示例有效,因为它只移动每次创建的局部变量。