如何为遵循迭代器模式的C函数处理Rust生命周期?
Rust封装C迭代器API的内存安全处理
问题背景
想用Rust封装一个C API,大部分场景运行正常,但遇到了C迭代器的内存管理问题。示例C代码如下:
#define _GNU_SOURCE #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> struct ExampleIter { const char **chain; char *current; }; bool example_iter_next(struct ExampleIter *iter) { free(iter->current); if (iter->chain[0] == NULL) { iter->current = NULL; return false; } iter->current = strdup(iter->chain[0]); iter->chain++; return true; } int main(void) { const char *chain[] = {"Hello", "World", "!", NULL}; struct ExampleIter iter = {.chain = chain, .current = NULL}; while (example_iter_next(&iter)) { printf("%s\n", iter.current); } return 0; }
核心问题:每次调用example_iter_next时,C迭代器的char *current指针会被释放失效。现在需要为该类型实现Rust的std::iter::Iterator trait,如何处理这种指针失效问题?
解决方案
1. 封装C迭代器为Rust类型
首先将C的ExampleIter用Rust结构体绑定,同时通过Drop trait确保迭代器销毁时的内存清理,避免泄漏:
use std::ffi::{CStr, CString}; use std::os::raw::c_char; use libc::free; // 与C结构体一一对应的Rust绑定,必须用#[repr(C)]保证内存布局一致 #[repr(C)] pub struct ExampleIter { chain: *const *const c_char, current: *mut c_char, } // 声明C函数的外部调用接口 extern "C" { fn example_iter_next(iter: *mut ExampleIter) -> bool; } // 封装后的Rust迭代器类型,持有C迭代器的所有权 pub struct ExampleIterator { inner: ExampleIter, } impl ExampleIterator { // 构造函数:传入已初始化的C迭代器 pub fn new(inner: ExampleIter) -> Self { Self { inner } } } // 实现Drop trait,销毁时释放最后一次分配的current内存 impl Drop for ExampleIterator { fn drop(&mut self) { unsafe { if !self.inner.current.is_null() { free(self.inner.current as *mut _); } } } }
2. 实现Iterator trait的正确姿势
在next方法中,每次调用C的example_iter_next后,立即将current指向的C字符串转换为Rust的String,避免持有悬垂指针:
impl Iterator for ExampleIterator { type Item = String; fn next(&mut self) -> Option<Self::Item> { unsafe { // 调用C的next函数,它会自动释放上一次的current let has_next = example_iter_next(&mut self.inner); if !has_next { return None; } // current此时是strdup分配的有效内存,读取并转换为Rust String let c_str = CStr::from_ptr(self.inner.current); let rust_str = c_str.to_str().expect("Invalid UTF-8 string from C API").to_string(); // 注意:这里不能手动释放current,下一次调用example_iter_next会自动处理 Some(rust_str) } } }
3. 关键注意事项
- 内存管理权移交:每次
example_iter_next调用后,current的内存所有权仍属于C迭代器,Rust只负责读取内容并复制到自身的String中,下一次调用C函数时会自动释放上一次的内存。 - 避免悬垂指针:绝对不能让Rust代码持有
current的裸指针超过单次next调用,因为下一次调用会直接释放该内存,导致悬垂指针。 - 安全操作边界:所有涉及C FFI和裸指针的代码必须放在
unsafe块中,同时要保证C迭代器的状态始终合法(比如不能在迭代器销毁后继续调用next)。
内容的提问来源于stack exchange,提问作者Gottox
相关产品推荐
相关产品推荐

