从一个循环中切换一个bool,然后用rust和gtk关闭Fn



我正试图用rust和gtk制作一款井字游戏,为了交换回合,当点击游戏按钮时,我会切换bool。由于rust/gtk集成,onclick事件在Fn闭包中运行,所以我不能直接编辑对bool的可变引用。

基于这篇文章,我从一个基本的bool移到了一个单元格中的bool,因为它具有内部可变性,允许它在闭包中变化。

然而,变量被移动到闭包中,在下一次循环/使用xturn时,我得到

--> 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.");
}

});
}
}

通过添加&导致相同的初始错误消息

在关闭开始时取消移动会导致此错误

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: &gtk::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属于在其中声明它的函数,但您在循环中创建的闭包将比该函数寿命更长,因此引用将变为无效

看起来您想要共享单个布尔的所有权,这是由Rc结构在Rust中实现的,该结构执行引用计数以确定何时可以销毁共享值。

然而,当同一共享值存在多个Rc时,Rc不允许可变借用,因此您仍然需要Cell的内部可变性。要使用的最后一种类型是Rc<Cell<bool>>。(这里不需要RefCell,因为您只需要能够获取和设置布尔值;您永远不需要对它的引用。(

您需要循环的每次迭代来移动Rc的副本。每个单独的Rc值将引用相同的Cell。例如,这个函数返回9个闭包,它们都做同样的事情:它们切换共享的布尔值,并将其与自己的索引一起返回到元组中:

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()
}

(游乐场(

最新更新