当前位置: 首页 > news >正文

Rust:DLL 输出对象的生命周期管理

在 Rust 开发 DLL 时安全地将对象地址传递给 C 语言并保持对象不被析构,需要正确处理所有权和生命周期。以下是详细方案和代码示例:

核心思路

  1. 堆分配对象 - 使用 Box 在堆上分配对象
  2. 泄漏所有权 - 使用 Box::into_raw 防止 Rust 自动析构
  3. 明确所有权协议 - 提供销毁函数由 C 语言调用方管理生命周期
  4. 线程安全 - 使用 Send + Sync 确保跨线程安全

完整示例

Rust DLL 实现
// src/lib.rs
use std::sync::Mutex;#[repr(C)]
pub struct Counter {value: Mutex<i32>,  // 内部可变性保证线程安全
}impl Counter {pub fn new(init: i32) -> Self {Counter {value: Mutex::new(init),}}pub fn increment(&self) {let mut lock = self.value.lock().unwrap();*lock += 1;}pub fn get_value(&self) -> i32 {*self.value.lock().unwrap()}
}// 创建对象并返回指针 (所有权转移给调用方)
#[no_mangle]
pub extern "C" fn counter_create(init: i32) -> *mut Counter {Box::into_raw(Box::new(Counter::new(init)))
}// 增加计数器值
#[no_mangle]
pub extern "C" fn counter_increment(counter: *mut Counter) {assert!(!counter.is_null());unsafe { &*counter }.increment();
}// 获取当前值
#[no_mangle]
pub extern "C" fn counter_get_value(counter: *const Counter) -> i32 {assert!(!counter.is_null());unsafe { &*counter }.get_value()
}// 销毁对象(必须由调用方显式调用)
#[no_mangle]
pub extern "C" fn counter_destroy(counter: *mut Counter) {if !counter.is_null() {unsafe { Box::from_raw(counter) }; // 转为 Box 后立即析构}
}
C 语言调用示例
#include <stdio.h>
#include <windows.h>typedef void* CounterHandle;// 声明 DLL 函数
CounterHandle counter_create(int init);
void counter_increment(CounterHandle counter);
int counter_get_value(CounterHandle counter);
void counter_destroy(CounterHandle counter);int main() {HMODULE dll = LoadLibrary("counter.dll");if (!dll) {fprintf(stderr, "Failed to load DLL\n");return 1;}// 获取函数指针CounterHandle (*create)(int) = (CounterHandle(*)(int))GetProcAddress(dll, "counter_create");void (*increment)(CounterHandle) = (void(*)(CounterHandle))GetProcAddress(dll, "counter_increment");int (*get_value)(CounterHandle) = (int(*)(CounterHandle))GetProcAddress(dll, "counter_get_value");void (*destroy)(CounterHandle) = (void(*)(CounterHandle))GetProcAddress(dll, "counter_destroy");// 创建计数器对象CounterHandle counter = create(10);// 使用计数器increment(counter);printf("Current value: %d\n", get_value(counter));  // 输出 11increment(counter);printf("Current value: %d\n", get_value(counter));  // 输出 12// 销毁对象destroy(counter);FreeLibrary(dll);return 0;
}

关键安全措施

  1. 所有权转移协议

    • counter_create → 将所有权转移给 C 调用方
    • counter_destroy → C 调用方交回所有权,触发析构
    • 所有其他操作(increment/get_value)只借用不获取所有权
  2. 空指针检查

    assert!(!counter.is_null());
    

    防止空指针解引用(在实际项目中可能需要更健壮的错误处理)

  3. 线程安全保证

    use std::sync::Mutex;
    

    使用互斥锁确保内部可变性是线程安全的

  4. 内存安全

    • Box::into_raw 转换后,Rust 停止管理生命周期
    • Box::from_raw 恢复所有权后立即析构
  5. FFI 安全

    #[repr(C)]
    pub struct Counter { ... }
    

    保证 C 兼容的内存布局

生命周期管理建议

  1. 强制销毁协议

    • 在文档中明确要求 C 调用方必须调用 counter_destroy
    • 可使用 RAII 封装(如 C++ 的 unique_ptr)管理对象生命周期
  2. 避免跨 DLL 边界析构

    • 所有内存操作应在同一 DLL 模块中进行
    • 建议对象创建和销毁都在 Rust DLL 中完成
  3. 版本兼容

    • 当修改对象结构时:
    #[repr(C)]
    pub struct CounterV2 {// 不要修改现有字段顺序value: Mutex<i32>,// 新增字段永远放在末尾max_value: i32, 
    }
    

替代方案:引用计数

如果需要共享所有权,可考虑 Arc

#[no_mangle]
pub extern "C" fn counter_create_arc(init: i32) -> *const Counter {Arc::into_raw(Arc::new(Counter::new(init)))
}#[no_mangle]
pub extern "C" fn counter_clone(counter: *const Counter) -> *const Counter {unsafe { Arc::increment_strong_count(counter) };counter
}#[no_mangle]
pub extern "C" fn counter_destroy_arc(counter: *const Counter) {unsafe { Arc::decrement_strong_count(counter) };
}

警告:使用引用计数时,调用方必须严格配对 clone/destroy 调用

最佳实践

  1. 使用 abi_stable - 提供更稳定的 ABI 接口
  2. 自动化测试 - 使用 cbindgen 生成头文件并编写 C/C++ 测试
  3. 内存分析 - 使用 Valgrind 或 Windows CRT 调试堆检测内存泄漏

通过这种方式,您可以安全地将 Rust 对象传递给 C,同时保持明确的所有权和生命周期管理。

http://www.dtcms.com/a/336395.html

相关文章:

  • 影刀初级B级考试大题2
  • STM32CUBEMX配置stm32工程
  • Linux学习-多任务(线程)
  • LangChain4j
  • 三分钟在VMware虚拟机安装winXP教程,开箱即用
  • HTTP0.9/1.0/1.1/2.0
  • linux下timerfd和posix timer为什么存在较大的抖动?
  • USB-A 3.2 和 USB-A 2.0的区别
  • 集成电路学习:什么是ORB方向性FAST和旋转BRIEF
  • 外贸电商选品方案的模型
  • 天地图应用篇: 增加缩放、比例尺控件
  • 集运业务突围:三大关键问题的智能化解决方案
  • 【数据结构与算法-Day 16】队列的应用:广度优先搜索(BFS)的基石与迷宫寻路实战
  • vulnhub-lampiao靶机渗透
  • 002.Redis 配置及数据类型
  • 安装pytorch3d后报和本机cuda不符
  • LLM、RAG、Agent知识点思维导图
  • 简单了解BeanFactory和FactoryBean的区别
  • AMBA-AXI and ACE协议详解(八)
  • Critic-V: VLM Critics Help Catch VLM Errors in Multimodal Reasoning(CVPR 2025)
  • C++零拷贝网络编程实战:从理论到生产环境的性能优化之路
  • Word和Excel的一些功能记录
  • PHP现代化全栈开发:测试驱动开发与持续交付实践
  • 重温k8s基础概念知识系列二(Pod)
  • 腾讯开源:视频生成框架Hunyuan-GameCraft
  • 说一下事件传播机制
  • LeeCode 39.组合总和
  • 第4章 鼎鼎大名的Reactor模式
  • Redis--day7--黑马点评--优惠券秒杀
  • steam_api64.dll丢失?steam_api64.dll修复工具