从一个循环中切换一个 bool 和 Fn losure with rust and gtk
Toggling a bool from within a loop and Fn losure with rust and gtk
我正在尝试用 rust 和 gtk 制作一个井字游戏,为了交换回合,我在单击游戏按钮时切换 bool。由于 rust/gtk 集成,Fn 闭包中的 onclick 事件 运行,所以我无法直接编辑对 bool 的可变引用。
基于this post,我从一个基本的 bool 移动到一个单元格内的 bool,因为它具有内部可变性,允许它在闭包内变化。
然而,变量被移动到闭包中,并且在 xturn 的 loop/usage 的下一次迭代中我得到
--> src/main.rs:45:44
|
39 | let xturn = Cell::new(true);
| ----- move occurs because `xturn` has type `std::cell::Cell<bool>`, which does not implement the `Copy` trait
...
45 | current_button.connect_clicked(move |current_button|{
| ^^^^^^^^^^^^^^^^^^^^^ value moved into closure here, in previous iteration of loop
46 | if current_button.label().unwrap() == ""{
47 | if xturn.get() {
| ----- use occurs due to use in closure
下面的代码
let game_grid: [[Button; 3];3] = [[new_game_button(), new_game_button(), new_game_button()],[new_game_button(), new_game_button(), new_game_button()],[new_game_button(), new_game_button(), new_game_button()]];
let xturn = Cell::new(true);
for i in 0..3{
for o in 0..3{
let current_button = &game_grid[i][o];
grid.attach(current_button, i.try_into().unwrap(), o.try_into().unwrap(), 1, 1);
current_button.connect_clicked(move |current_button|{
if current_button.label().unwrap() == ""{
if xturn.get() {
current_button.set_label("X");
} else{
current_button.set_label("O");
};
xturn.set(!xturn.get());
} else{
println!("This spot is taken! Go Again.");
}
});
}
}
通过添加 & 导致相同的初始错误消息来创建对 xturn 的每次使用的引用
在闭包开始时取消移动会导致此错误
error[E0373]: closure may outlive the current function, but it borrows `xturn`, which is owned by the current function
完整代码
use std::cell::Cell;
use gtk4 as gtk;
use gtk::{prelude::*, Label};
use gtk::{Align, Application, ApplicationWindow, Button, Grid};
fn main() {
let app = Application::builder()
.application_id("org.beribus.tictactoe")
.build();
app.connect_activate(build_ui);
println!("Hello, world!");
app.run();
}
fn build_ui(app: >k::Application){
let window = ApplicationWindow::builder()
.application(app)
.default_width(360)
.default_height(360)
.title("GTK Tac Toe")
.build();
let grid = Grid::builder()
.margin_top(10)
.margin_bottom(10)
.margin_start(10)
.margin_end(10)
.halign(gtk::Align::Center)
.valign(gtk::Align::Center)
.row_spacing(6)
.column_spacing(6)
.build();
window.set_child(Some(&grid));
let game_grid: [[Button; 3];3] = [[new_game_button(), new_game_button(), new_game_button()],[new_game_button(), new_game_button(), new_game_button()],[new_game_button(), new_game_button(), new_game_button()]];
let xturn = Cell::new(true);
for i in 0..3{
for o in 0..3{
let current_button = &game_grid[i][o];
grid.attach(current_button, i.try_into().unwrap(), o.try_into().unwrap(), 1, 1);
current_button.connect_clicked(move |current_button|{
if current_button.label().unwrap() == ""{
if xturn.get() {
current_button.set_label("X");
} else{
current_button.set_label("O");
};
&xturn.set(!&xturn.get());
} else{
println!("This spot is taken! Go Again.");
}
});
}
}
let text = Label::builder()
.label("woro")
.build();
if xturn.get(){
text.set_label("yoyooyo it's X's turn");
} else{
text.set_label("yoyooyo it's O's turn");
}
grid.attach(&text, 0,4,3,1);
let reset_button = Button::builder()
.halign(Align::Center)
.valign(Align::Center)
.label("Reset Game")
.build();
reset_button.connect_clicked(move |_|{
for i in 0..3{
for o in 0..3{
let current_button = &game_grid[i][o];
current_button.set_label("");
}
}
});
grid.attach(&reset_button, 0,3,3,1);
window.show();
}
fn new_game_button() -> Button{
let button = Button::builder()
.halign(Align::Center)
.valign(Align::Center)
.label("")
.width_request(90)
.height_request(90)
.build();
button
}
如编译器所示,这两种方法都有缺陷。
- 当您使用
move
方法时,您是在告诉闭包它可以有 xturn
,但由于这是一个循环,它可以执行多次。您不能将相同的值移动两次(因为它在第一次后就消失了),因此不允许第二次移动。
- 当您尝试通过引用捕获时,编译器会告诉您
xturn
属于声明它的函数,但是您在循环中创建的闭包将比该函数更有效,因此引用将失效。
看起来你想要 共享所有权 单个 bool,它在 Rust 中由 Rc
结构实现,它执行 reference-counting 来确定什么时候可以销毁共享值。
然而,Rc
不允许可变借用,而同一共享值存在多个 Rc
,因此您仍然需要 Cell
的内部可变性。您要使用的最终类型是 Rc<Cell<bool>>
。 (此处不需要 RefCell
,因为您只需要能够获取和设置 bool 值;您永远不需要对它的引用。)
您需要循环的每次迭代来移动 Rc
的副本。每个单独的 Rc
值将引用相同的 Cell
。例如,这个函数 returns 9 个闭包都做同样的事情:它们切换共享布尔值和 return 它在一个元组中连同它们自己的索引:
fn create_closures() -> Vec<Box<dyn Fn() -> (i32, bool)>> {
let xturn = Rc::new(Cell::new(false));
(0..9).map(|i| -> Box<dyn Fn() -> (i32, bool)> {
// Make a clone of the Rc that this closure can steal.
let xturn = xturn.clone();
Box::new(move || {
let v = !xturn.get();
xturn.set(v);
(i, v)
})
}).collect()
}
我正在尝试用 rust 和 gtk 制作一个井字游戏,为了交换回合,我在单击游戏按钮时切换 bool。由于 rust/gtk 集成,Fn 闭包中的 onclick 事件 运行,所以我无法直接编辑对 bool 的可变引用。
基于this post,我从一个基本的 bool 移动到一个单元格内的 bool,因为它具有内部可变性,允许它在闭包内变化。
然而,变量被移动到闭包中,并且在 xturn 的 loop/usage 的下一次迭代中我得到
--> src/main.rs:45:44
|
39 | let xturn = Cell::new(true);
| ----- move occurs because `xturn` has type `std::cell::Cell<bool>`, which does not implement the `Copy` trait
...
45 | current_button.connect_clicked(move |current_button|{
| ^^^^^^^^^^^^^^^^^^^^^ value moved into closure here, in previous iteration of loop
46 | if current_button.label().unwrap() == ""{
47 | if xturn.get() {
| ----- use occurs due to use in closure
下面的代码
let game_grid: [[Button; 3];3] = [[new_game_button(), new_game_button(), new_game_button()],[new_game_button(), new_game_button(), new_game_button()],[new_game_button(), new_game_button(), new_game_button()]];
let xturn = Cell::new(true);
for i in 0..3{
for o in 0..3{
let current_button = &game_grid[i][o];
grid.attach(current_button, i.try_into().unwrap(), o.try_into().unwrap(), 1, 1);
current_button.connect_clicked(move |current_button|{
if current_button.label().unwrap() == ""{
if xturn.get() {
current_button.set_label("X");
} else{
current_button.set_label("O");
};
xturn.set(!xturn.get());
} else{
println!("This spot is taken! Go Again.");
}
});
}
}
通过添加 & 导致相同的初始错误消息来创建对 xturn 的每次使用的引用
在闭包开始时取消移动会导致此错误
error[E0373]: closure may outlive the current function, but it borrows `xturn`, which is owned by the current function
完整代码
use std::cell::Cell;
use gtk4 as gtk;
use gtk::{prelude::*, Label};
use gtk::{Align, Application, ApplicationWindow, Button, Grid};
fn main() {
let app = Application::builder()
.application_id("org.beribus.tictactoe")
.build();
app.connect_activate(build_ui);
println!("Hello, world!");
app.run();
}
fn build_ui(app: >k::Application){
let window = ApplicationWindow::builder()
.application(app)
.default_width(360)
.default_height(360)
.title("GTK Tac Toe")
.build();
let grid = Grid::builder()
.margin_top(10)
.margin_bottom(10)
.margin_start(10)
.margin_end(10)
.halign(gtk::Align::Center)
.valign(gtk::Align::Center)
.row_spacing(6)
.column_spacing(6)
.build();
window.set_child(Some(&grid));
let game_grid: [[Button; 3];3] = [[new_game_button(), new_game_button(), new_game_button()],[new_game_button(), new_game_button(), new_game_button()],[new_game_button(), new_game_button(), new_game_button()]];
let xturn = Cell::new(true);
for i in 0..3{
for o in 0..3{
let current_button = &game_grid[i][o];
grid.attach(current_button, i.try_into().unwrap(), o.try_into().unwrap(), 1, 1);
current_button.connect_clicked(move |current_button|{
if current_button.label().unwrap() == ""{
if xturn.get() {
current_button.set_label("X");
} else{
current_button.set_label("O");
};
&xturn.set(!&xturn.get());
} else{
println!("This spot is taken! Go Again.");
}
});
}
}
let text = Label::builder()
.label("woro")
.build();
if xturn.get(){
text.set_label("yoyooyo it's X's turn");
} else{
text.set_label("yoyooyo it's O's turn");
}
grid.attach(&text, 0,4,3,1);
let reset_button = Button::builder()
.halign(Align::Center)
.valign(Align::Center)
.label("Reset Game")
.build();
reset_button.connect_clicked(move |_|{
for i in 0..3{
for o in 0..3{
let current_button = &game_grid[i][o];
current_button.set_label("");
}
}
});
grid.attach(&reset_button, 0,3,3,1);
window.show();
}
fn new_game_button() -> Button{
let button = Button::builder()
.halign(Align::Center)
.valign(Align::Center)
.label("")
.width_request(90)
.height_request(90)
.build();
button
}
如编译器所示,这两种方法都有缺陷。
- 当您使用
move
方法时,您是在告诉闭包它可以有xturn
,但由于这是一个循环,它可以执行多次。您不能将相同的值移动两次(因为它在第一次后就消失了),因此不允许第二次移动。 - 当您尝试通过引用捕获时,编译器会告诉您
xturn
属于声明它的函数,但是您在循环中创建的闭包将比该函数更有效,因此引用将失效。
看起来你想要 共享所有权 单个 bool,它在 Rust 中由 Rc
结构实现,它执行 reference-counting 来确定什么时候可以销毁共享值。
然而,Rc
不允许可变借用,而同一共享值存在多个 Rc
,因此您仍然需要 Cell
的内部可变性。您要使用的最终类型是 Rc<Cell<bool>>
。 (此处不需要 RefCell
,因为您只需要能够获取和设置 bool 值;您永远不需要对它的引用。)
您需要循环的每次迭代来移动 Rc
的副本。每个单独的 Rc
值将引用相同的 Cell
。例如,这个函数 returns 9 个闭包都做同样的事情:它们切换共享布尔值和 return 它在一个元组中连同它们自己的索引:
fn create_closures() -> Vec<Box<dyn Fn() -> (i32, bool)>> {
let xturn = Rc::new(Cell::new(false));
(0..9).map(|i| -> Box<dyn Fn() -> (i32, bool)> {
// Make a clone of the Rc that this closure can steal.
let xturn = xturn.clone();
Box::new(move || {
let v = !xturn.get();
xturn.set(v);
(i, v)
})
}).collect()
}