You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

移植含可变借用的C代码时遇运行时错误(nappgui示例)

问题描述

我有系统编程领域的C/C++开发背景,目前正在转向Rust语言。完成Rust官方手册与Rustlings课程后,我决定移植nappgui库来巩固对Rust的理解。

nappgui中频繁使用一种涉及可变借用的模式,转译为Rust代码后虽能编译,但触发了可变借用相关的运行时错误。以下是最小复现代码:

//////////////////////////////////////////////////////////
// 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);
}

运行输出:

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

我理解错误原因,希望获得重构代码以避免该错误的指导,尤其欢迎针对C++可变借用模式转译为Rust的相关见解。

补充说明:上述代码对应项目中广泛使用的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;
}

解决方案

1. 核心问题分析

错误出在osmain函数中:

  • let tmp_c = &app.as_ref().borrow_mut().osapp; 对App发起了可变借用,这个借用的生命周期会持续到if let块结束
  • 当run调用on_finish_launch闭包时,闭包内尝试对App发起不可变借用,此时可变借用尚未释放,触发Rust的借用规则冲突,导致运行时BorrowError

2. 重构代码

调整osmain函数,提前获取OSApp的引用并缩短可变借用的生命周期:

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);

    // 用代码块限制可变借用的生命周期,存入osapp后立即释放
    {
        let mut app_mut = app.borrow_mut();
        app_mut.osapp = Some(osapp);
    }

    let tmp_b = app.clone();
    let mut on_finish_launch = move || {
        // 此时App的可变借用已释放,可安全获取不可变借用
        let app_ref = tmp_b.borrow();
        i_OnFinishLaunching(&app_ref);
    };

    // 获取OSApp的不可变引用,此时无活跃的可变借用
    let app_ref = app.borrow();
    if let Some(osapp) = &app_ref.osapp {
        run(osapp, &mut on_finish_launch);
    }
}

3. C/C++模式转译到Rust的关键思路

C/C++中常用的"对象持有自身指针给回调"模式,在Rust中需要调整思路适配借用规则:

  • 缩短可变借用生命周期:用代码块{}手动控制可变借用的作用域,避免长时间持有
  • 拆分数据依赖:如果回调仅需要对象的部分数据,将这部分数据单独封装,减少对整个对象的借用需求
  • 合理使用内部可变性:Rc<RefCell<T>>是模拟C++可变指针的常用方式,但要避免嵌套借用,回调内尽量只做读取操作
  • 优先考虑所有权转移:若对象无需在回调后复用,直接转移所有权,彻底避免借用冲突

4. 进一步优化建议

针对项目中大量存在的回调场景,可做如下优化:

  • 初始化时直接绑定App与OSApp的关系,避免后续的可变操作
  • 用Weak<RefCell<T>>传递给回调,规避潜在的循环引用风险
  • 封装回调注册逻辑,统一管理对象的借用状态,确保回调执行时的安全性

内容的提问来源于stack exchange,提问作者Dushara

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 05:47:04