Rust:为什么我不能移动一个值两次?
Rust: Why can't I move a value twice?
Rust 的新手,真的觉得我在这里遗漏了一些东西。
在下面的代码中,有两个对问题很重要的嵌套闭包:app.connect_activate
和 draw_area.connect_draw
。我正在尝试使用通道将值发送到 connect_draw
闭包中,以便绘制的内容将反映随时间变化的模拟状态(当前由随机颜色表示)。
我发现的东西对我来说很奇怪,我根本不明白:
- 如果我在
connect_activate
内创建频道,我可以在 connect_draw
内调用 rx.recv
(这是下面的第一个示例)
- 如果我在
connect_activate
之外创建频道,我可以在 connect_activate
内调用 rx.recv
(为简洁起见省略示例)
- 如果我在
connect_activate
外创建频道,我 不能 在 connect_draw
内调用 rx.recv
;它失败并显示 cannot move out of rx, a captured variable in an Fn closure
(这是下面的第二个示例)
换句话说:我可以将创建的通道移动到任一闭包边界,但不能同时移动。它可以从 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
参数,这意味着它可以被多次调用。您不能 move
从 Fn
闭包中捕获变量,因为该变量将在您下次尝试调用它时被移动。第一个示例有效,因为它只移动每次创建的局部变量。
Rust 的新手,真的觉得我在这里遗漏了一些东西。
在下面的代码中,有两个对问题很重要的嵌套闭包:app.connect_activate
和 draw_area.connect_draw
。我正在尝试使用通道将值发送到 connect_draw
闭包中,以便绘制的内容将反映随时间变化的模拟状态(当前由随机颜色表示)。
我发现的东西对我来说很奇怪,我根本不明白:
- 如果我在
connect_activate
内创建频道,我可以在connect_draw
内调用rx.recv
(这是下面的第一个示例) - 如果我在
connect_activate
之外创建频道,我可以在connect_activate
内调用rx.recv
(为简洁起见省略示例) - 如果我在
connect_activate
外创建频道,我 不能 在connect_draw
内调用rx.recv
;它失败并显示cannot move out of rx, a captured variable in an Fn closure
(这是下面的第二个示例)
换句话说:我可以将创建的通道移动到任一闭包边界,但不能同时移动。它可以从 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
参数,这意味着它可以被多次调用。您不能 move
从 Fn
闭包中捕获变量,因为该变量将在您下次尝试调用它时被移动。第一个示例有效,因为它只移动每次创建的局部变量。