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

如何为遵循迭代器模式的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 23:46:16