我正在从系统编程的C/C++背景过渡到Rust.完成官方书籍和Rustlings课程后,我决定移植nappgui库(https://github.com/frang75/nappgui),以巩固我对Rust的理解.
nappgui中经常使用一种特定模式,我很难将其有效地翻译为Rust.该模式涉及可变borrow ,当我当前的实现进行编译时,它会引发与这些borrow 相关的运行时错误.
这是我最后try 的一个最小示例(尽管它可能不能完美反映原始的nappgui代码):
//////////////////////////////////////////////////////////
// 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
我很感激有关如何重新设计或实现代码以避免这些可变borrow 错误的指导.任何特定于移植涉及可变borrow 到Rust的C++模式的见解都会特别有帮助.
Update
上面是整个项目中使用的建筑模式的实例.C中的一般模式如下:
#include <stdio.h>
#include <stdlib.h>
typedef void(*CB1_T)(void*) ;
typedef void(*CB2_T)(void*) ;
struct LowLevelObject {
void *listener;
// state data
CB1_T callback1;
CB2_T callback2;
};
void low_api1(struct LowLevelObject *lo)
{
// some functionality
lo->callback1(lo->listener);
// more functionality
}
void low_api2(struct LowLevelObject *lo)
{
// some functionality
lo->callback2(lo->listener);
// more functionality
}
void low_api3(struct LowLevelObject *lo)
{
// some functionality
printf("%s\n", __func__);
}
struct LowLevelObject *low_create(
void *listener,
CB1_T callback1,
CB2_T callback2)
{
struct LowLevelObject *lo = calloc(1, sizeof(struct LowLevelObject));
lo->listener = listener;
lo->callback1 = callback1;
lo->callback2 = callback2;
return lo;
}
void low_destroy(struct LowLevelObject *lo)
{
free(lo);
}
/////////////////////////////////////////
struct HighLevelObject {
struct LowLevelObject *low;
// State data
};
static void on_callback1(struct HighLevelObject *hi)
{
printf("%s\n", __func__);
low_api3(hi->low);
}
static void on_callback2(struct HighLevelObject *hi)
{
printf("%s\n", __func__);
low_api3(hi->low);
}
struct HighLevelObject *high_create()
{
struct HighLevelObject *hi = calloc(1, sizeof(struct HighLevelObject));
hi->low = low_create(hi, (CB1_T)on_callback1, (CB2_T)on_callback2);
// NULL checks ignored for simplicity
return hi;
}
void high_destroy(struct HighLevelObject *hi)
{
low_destroy(hi->low);
free(hi);
}
void hi_start()
{
struct HighLevelObject *hi = high_create();
low_api1(hi->low);
low_api2(hi->low);
high_destroy(hi);
}
////////////////////////////////
int main() {
hi_start();
return 0;
}