Я начал изучать Rust и хотел бы создать очень простого тральщика. Я думаю, что я закончил с игровой логикой, и я работаю над GUI. У меня проблема с обработкой клика по полям. Когда пользователь нажимает на одно из полей, он вызывает две вещи:
- Обновление игрового состояния (в настоящее время только открытие соответствующего поля)
- Запуск перестроения сетки, визуализирующей фактическоесостояние игры. Эта сетка содержит виджет, который обрабатывает событие нажатия.
Я удалил обработку логики игры, так как она не сильно связана с возникшей проблемой:
Мой Cargo.toml:
[package]
name = "orbtk_grid_event_handler"
version = "0.1.0"
edition = "2018"
[dependencies]
orbtk = "0.2.29"
orbimage = "0.1.17"
orbclient = "0.3.17"
Мой main.rs:
use std::cell::RefCell;
use std::rc::Rc;
use std::sync::Arc;
extern crate orbimage;
extern crate orbtk;
use orbtk::traits::Click;
use orbtk::{Color, Grid, Image, Place, Rect, Window};
struct MainWindowInner {
grid: Arc<Grid>,
}
impl MainWindowInner {
fn new() -> Self {
MainWindowInner { grid: Grid::new() }
}
}
pub struct MainWindow {
inner: Rc<RefCell<MainWindowInner>>,
}
impl MainWindow {
pub fn new() -> Self {
MainWindow {
inner: Rc::new(RefCell::new(MainWindowInner::new())),
}
}
pub fn show(&self) {
let mut window = Window::new(Rect::new(100, 100, 420, 768), "Minesweeper");
{
let inner = self.inner.borrow();
inner.grid.position(0, 0).spacing(1, 1);
window.add(&inner.grid);
Self::update_grid(self.inner.clone());
}
window.exec();
}
fn update_grid(inner_param: Rc<RefCell<MainWindowInner>>) {
let inner = inner_param.borrow();
let grid = inner.grid.clone();
grid.insert(0, 0, &Self::create_cell(inner_param.clone()));//
}
fn create_cell(inner_param: Rc<RefCell<MainWindowInner>>) -> Arc<Image> {
let cell = Image::from_color(16, 16, Color::rgb(0, 0, 255));
cell.on_click(move |_cell, _point| {
MainWindow::update_grid(inner_param.clone()); // Panic happens inside this call
});
return cell;
}
}
fn main() {
MainWindow::new().show();
}
И обратная трассировка стека выглядит следующим образом:
thread 'main' panicked at 'already borrowed: BorrowMutError', src/libcore/result.rs:1165:5
stack backtrace:
0: backtrace::backtrace::libunwind::trace
at /cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.37/src/backtrace/libunwind.rs:88
1: backtrace::backtrace::trace_unsynchronized
at /cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.37/src/backtrace/mod.rs:66
2: std::sys_common::backtrace::_print_fmt
at src/libstd/sys_common/backtrace.rs:77
3: <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt
at src/libstd/sys_common/backtrace.rs:61
4: core::fmt::write
at src/libcore/fmt/mod.rs:1028
5: std::io::Write::write_fmt
at src/libstd/io/mod.rs:1412
6: std::sys_common::backtrace::_print
at src/libstd/sys_common/backtrace.rs:65
7: std::sys_common::backtrace::print
at src/libstd/sys_common/backtrace.rs:50
8: std::panicking::default_hook::{{closure}}
at src/libstd/panicking.rs:189
9: std::panicking::default_hook
at src/libstd/panicking.rs:206
10: std::panicking::rust_panic_with_hook
at src/libstd/panicking.rs:469
11: std::panicking::continue_panic_fmt
at src/libstd/panicking.rs:376
12: rust_begin_unwind
at src/libstd/panicking.rs:303
13: core::panicking::panic_fmt
at src/libcore/panicking.rs:84
14: core::result::unwrap_failed
at src/libcore/result.rs:1165
15: core::result::Result<T,E>::expect
at /rustc/c23a7aa778b0dfeffbf83b099bdf971242c1e1ac/src/libcore/result.rs:960
16: core::cell::RefCell<T>::borrow_mut
at /rustc/c23a7aa778b0dfeffbf83b099bdf971242c1e1ac/src/libcore/cell.rs:869
17: orbtk::layouts::grid::Grid::insert
at /home/kovi/.cargo/registry/src/github.com-1ecc6299db9ec823/orbtk-0.2.29/src/layouts/grid.rs:67
18: orbtk_grid_event_handler::MainWindow::update_grid
at src/main.rs:48
19: orbtk_grid_event_handler::MainWindow::create_cell::{{closure}}
at src/main.rs:55
20: <orbtk::primitives::image::Image as orbtk::traits::click::Click>::emit_click
at /home/kovi/.cargo/registry/src/github.com-1ecc6299db9ec823/orbtk-0.2.29/src/primitives/image.rs:56
21: <orbtk::primitives::image::Image as orbtk::widgets::Widget>::event
at /home/kovi/.cargo/registry/src/github.com-1ecc6299db9ec823/orbtk-0.2.29/src/primitives/image.rs:106
22: <orbtk::layouts::grid::Grid as orbtk::widgets::Widget>::event
at /home/kovi/.cargo/registry/src/github.com-1ecc6299db9ec823/orbtk-0.2.29/src/layouts/grid.rs:197
23: orbtk::window::Window::drain_events
at /home/kovi/.cargo/registry/src/github.com-1ecc6299db9ec823/orbtk-0.2.29/src/window.rs:230
24: orbtk::window::Window::exec
at /home/kovi/.cargo/registry/src/github.com-1ecc6299db9ec823/orbtk-0.2.29/src/window.rs:302
25: orbtk_grid_event_handler::MainWindow::show
at src/main.rs:41
26: orbtk_grid_event_handler::main
at src/main.rs:62
27: std::rt::lang_start::{{closure}}
at /rustc/c23a7aa778b0dfeffbf83b099bdf971242c1e1ac/src/libstd/rt.rs:61
28: std::rt::lang_start_internal::{{closure}}
at src/libstd/rt.rs:48
29: std::panicking::try::do_call
at src/libstd/panicking.rs:288
30: __rust_maybe_catch_panic
at src/libpanic_unwind/lib.rs:80
31: std::panicking::try
at src/libstd/panicking.rs:267
32: std::panic::catch_unwind
at src/libstd/panic.rs:396
33: std::rt::lang_start_internal
at src/libstd/rt.rs:47
34: std::rt::lang_start
at /rustc/c23a7aa778b0dfeffbf83b099bdf971242c1e1ac/src/libstd/rt.rs:61
35: main
36: __libc_start_main
37: _start
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
Когда я нажимаю на поле, вызывается закрытие обработчика событияВ этом закрытии я обрабатываю состояние игры и после этого пытаюсь обновить графический интерфейс. Паника возникает из функции grid.insert
в функции update_grid
: я добавил два комментария, чтобы быть более конкретным.
Паника, вызванная внутренней структурой данных сетки: поле entries
заимствовано во времяобработка событий, поэтому его нельзя изменить из обработчика событий (функция insert
определенно изменяет его).
Как я могу решить эту проблему? Хорошим решением было бы как-то извлечь обновление GUI из обработчика событий, но я не могу найти, как это сделать.