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

通过Dart FFI调用Rust Copy_Dir函数时出现内存分配失败问题

Dart调用Rust目录拷贝函数触发超大内存分配错误的解决方案

问题描述

从Dart调用Rust实现的copy_dir函数时,出现错误:memory allocation of 140374366121105 bytes failed,设备连接丢失,但实际待拷贝的源文件夹仅2.4KB。

错误根源分析

  1. FFI函数签名不匹配:原Rust函数直接接收&Path参数,但Dart通过FFI传递的是C风格UTF-8字符串指针,Rust无法正确解析这种指针到Path引用,导致内存结构被错误解读,进而触发不合理的超大内存分配请求。
  2. 目录拷贝逻辑错误:原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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 07:07:50