通过Dart FFI调用Rust Copy_Dir函数时出现内存分配失败问题
Dart调用Rust目录拷贝函数触发超大内存分配错误的解决方案
问题描述
从Dart调用Rust实现的copy_dir函数时,出现错误:memory allocation of 140374366121105 bytes failed,设备连接丢失,但实际待拷贝的源文件夹仅2.4KB。
错误根源分析
- FFI函数签名不匹配:原Rust函数直接接收
&Path参数,但Dart通过FFI传递的是C风格UTF-8字符串指针,Rust无法正确解析这种指针到Path引用,导致内存结构被错误解读,进而触发不合理的超大内存分配请求。 - 目录拷贝逻辑错误:原Rust代码处理子目录时,错误地创建源目录的副本而非目标目录的子目录,且缺少递归拷贝逻辑,无法处理嵌套目录。
修复方案
1. 修正Rust端代码
需要将Rust函数适配FFI调用规范,正确处理C字符串指针,并修复拷贝逻辑:
use std::ffi::{CStr, OsStr}; use std::os::unix::ffi::OsStrExt; // Unix系统适用,Windows请替换为`std::os::windows::ffi::OsStrExt` use std::path::Path; use std::fs; use std::io; use libc::c_char; // 暴露给FFI的函数,使用C调用约定,关闭名称混淆 #[no_mangle] pub extern "C" fn copy_dir(source: *const c_char, destination: *const c_char) -> i32 { // 安全解析C字符串指针 let source_cstr = unsafe { if source.is_null() { return -1; // 空指针错误码 } CStr::from_ptr(source) }; let dest_cstr = unsafe { if destination.is_null() { return -2; // 空指针错误码 } CStr::from_ptr(destination) }; // 转换为Rust Path类型 let source_path = Path::new(OsStr::from_bytes(source_cstr.to_bytes())); let dest_path = Path::new(OsStr::from_bytes(dest_cstr.to_bytes())); // 执行拷贝逻辑并返回错误码 match copy_dir_impl(source_path, dest_path) { Ok(_) => 0, // 成功返回0 Err(e) => { eprintln!("拷贝错误: {}", e); -3 // 拷贝失败错误码 } } } // 内部目录拷贝实现,修复递归逻辑 fn copy_dir_impl(source: &Path, destination: &Path) -> io::Result<()> { if source.is_dir() { fs::create_dir_all(destination)?; for entry in fs::read_dir(source)? { let entry = entry?; let entry_path = entry.path(); let dest_entry_path = destination.join(entry.file_name()); if entry_path.is_dir() { // 递归拷贝子目录 copy_dir_impl(&entry_path, &dest_entry_path)?; } else { fs::copy(&entry_path, &dest_entry_path)?; } } } else { fs::copy(source, destination)?; } Ok(()) }
2. 修正Dart端代码
更新函数签名以匹配Rust的返回值,并增加错误码检查:
import 'dart:ffi'; import 'dart:io'; import 'package:ffi/ffi.dart'; typedef CopyDirFunc = Int32 Function( Pointer<Utf8> source, Pointer<Utf8> destination, ); typedef CopyDir = int Function( Pointer<Utf8> source, Pointer<Utf8> destination, ); void main(){ final path = Platform.isWindows ? 'path\\to\\mylib.dll' : '/path/to/mylib.so'; final dylib = DynamicLibrary.open(path); final copyDirPointer = dylib.lookup<NativeFunction<CopyDirFunc>>('copy_dir'); final copyDir = copyDirPointer.asFunction<CopyDir>(); const source = '/path/from/ag_fsm_docs'; final utf8Pointer = source.toNativeUtf8(); const destination = '/path/to/rust_play_folder/Jahid'; final utf8Pointer2 = destination.toNativeUtf8(); // 调用并检查结果 final result = copyDir(utf8Pointer, utf8Pointer2); if (result == 0) { print('目录拷贝成功'); } else { print('拷贝失败,错误码: $result'); } // 释放内存 calloc.free(utf8Pointer); calloc.free(utf8Pointer2); }
关键修复点说明
- FFI适配:Rust函数必须使用
#[no_mangle]和extern "C"确保Dart能正确找到并调用;通过CStr安全解析传入的指针,避免内存错误。 - 递归逻辑修复:处理子目录时,递归调用拷贝函数而非错误创建原目录,确保嵌套目录能被正确拷贝。
- 错误处理:通过返回错误码让Dart能感知拷贝结果,避免静默崩溃。
内容的提问来源于stack exchange,提问作者Md. Jahid Hasan
相关产品推荐
相关产品推荐

