I’m transitioning to Rust from a C/C++ background in systems programming. After finishing the official book and Rustlings course, I decided to port the nappgui library (https://github.com/frang75/nappgui) as a way to solidify my Rust understanding.
There’s a specific pattern used frequently in nappgui that I’m having trouble translating effectively to Rust. The pattern involves mutable borrows, and while my current implementation compiles, it throws runtime errors related to these borrows.
Here’s a minimal example of my final attempt (although it might not perfectly reflect the original nappgui code):
//////////////////////////////////////////////////////////
// Platform specific OSAPP library crate
// e.g. osapp_win.rs
pub struct OSApp {
abnormal_termination: bool,
with_run_loop: bool,
}
pub fn init_imp(with_run_loop: bool) -> Box<OSApp> {
Box::new(OSApp {
abnormal_termination: false,
with_run_loop: with_run_loop,
})
}
pub fn run(app: &OSApp, on_finish_launching: &mut dyn FnMut()) {
on_finish_launching();
if app.with_run_loop {
// Following line commented out to simplify
// osgui::message_loop();
i_terminate(app);
}
}
fn i_terminate(_app: &OSApp) {
// Calls more client callbacks
}
//////////////////////////////////////////////////////////
// OSAPP crate
use core::f64;
use std::{cell::RefCell, rc::Rc};
struct App {
osapp: Option<Box<OSApp>>,
_lframe: f64,
func_create: FnAppCreate,
}
pub trait ClientObject {}
type FnAppCreate = fn() -> Box<dyn ClientObject>;
pub fn osmain(lframe: f64, func_create: FnAppCreate) {
let app = Rc::new(RefCell::new(App {
osapp: None,
_lframe: lframe,
func_create: func_create,
}));
let osapp: Box<OSApp> = osapp_init(true);
let tmp_a = app.clone();
tmp_a.as_ref().borrow_mut().osapp = Some(osapp);
let tmp_b = app.clone();
let on_finish_launch = || {
// I understand why I get the already borrowed mutable error here
i_OnFinishLaunching(&tmp_b.as_ref().borrow());
// ^^^^^^^^^^^^^^^^^^^^^^^^^
};
let tmp_c = &app.as_ref().borrow_mut().osapp;
if let Some(osapp) = tmp_c {
/*osapp::*/
run(&osapp, &mut &on_finish_launch);
}
}
fn osapp_init(with_run_loop: bool) -> Box<OSApp> {
/*osapp::*/
init_imp(with_run_loop)
}
fn i_OnFinishLaunching(app: &App) {
(app.func_create)();
}
//////////////////////////////////////////////////////////
// main.rs
struct Application {
// widgets go here
}
impl ClientObject for Application {}
impl Application {
fn create() -> Box<dyn ClientObject> {
let mut app = Box::new(Application {
// Create all the widgets here
});
app
}
}
fn main() {
/*osapp::*/
osmain(0.0, Application::create);
}
Output:
thread 'main' panicked at src/main.rs:55:45:
already mutably borrowed: BorrowError
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
I’d appreciate some guidance on how to redesign or implement the code to avoid these mutable borrow errors. Any insights specific to porting C++ patterns involving mutable borrows to Rust would be especially helpful.
3