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

如何修复Rust中fs::read_dir遍历目录时的所有权移动错误?

Rust递归读取目录的所有权问题修复

问题代码

你编写的递归读取目录的代码如下:

pub async fn list_folder() {
    let program_data_folder = String::from(std::env::var("ProgramData").expect("err") + "/Microsoft/Windows/Start Menu/Programs");
    let path = Path::new(&program_data_folder);
    let paths = fs::read_dir(path).unwrap();

    for folder in paths {
        println!("Name: {}", folder.unwrap().path().display());
        if folder.unwrap().path().is_dir() {
            fs::read_dir(folder.unwrap().file_name()).unwrap();
        }
    }
}

报错信息

编译时出现所有权错误:

use of moved value: `folder`
value used here after moverustcClick for full compiler diagnostic
listeners.rs(15, 37): `folder` moved due to this method call
listeners.rs(15, 30): help: consider calling `.as_ref()` or `.as_mut()` to borrow the type's contents
listeners.rs(14, 9): move occurs because `folder` has type `Result<DirEntry, std::io::Error>`, which does not implement the `Copy` trait
result.rs(1106, 19): `Result::<T, E>::unwrap` takes ownership of the receiver `self`, which moves `folder`

错误原因

Result<T, E>没有实现Copy trait,每次调用folder.unwrap()都会转移folder的所有权——第一次调用后,folder变量就不再有效,后续再调用folder.unwrap()自然会触发“使用已移动值”的错误。此外原代码没有真正实现递归逻辑,只是读取了子目录但没有继续遍历。

修复方案

  1. 提前将Result<DirEntry, Error> unwrap一次,保存到变量中,后续操作都基于这个变量,避免重复移动所有权。
  2. 修改函数为接收路径参数,支持递归调用。
  3. 同步/异步逻辑对齐:原函数标记了async但使用了标准库的同步文件操作,要么移除async,要么改用异步文件库(如tokio::fs)。

同步版本修复代码

use std::fs;
use std::path::Path;

// 改为接收路径参数,支持递归调用
pub fn list_folder(path: &Path) {
    // 读取目录,可根据需求替换为unwrap_or_else做更优雅的错误处理
    let entries = fs::read_dir(path).expect("读取目录失败");

    for entry_res in entries {
        // 只unwrap一次,获取DirEntry实例
        let entry = entry_res.expect("读取目录项失败");
        let entry_path = entry.path();
        
        println!("Name: {}", entry_path.display());

        // 如果是子目录,递归调用自身
        if entry_path.is_dir() {
            list_folder(&entry_path);
        }
    }
}

pub fn main() {
    // 用Path::join拼接路径,比字符串拼接更安全,自动适配平台路径分隔符
    let program_data = std::env::var("ProgramData").expect("无法获取ProgramData环境变量");
    let target_path = Path::new(&program_data).join("Microsoft/Windows/Start Menu/Programs");
    
    list_folder(&target_path);
}

异步版本修复代码(基于tokio)

use tokio::fs;
use std::path::Path;

pub async fn list_folder(path: &Path) {
    let entries = fs::read_dir(path).await.expect("读取目录失败");

    for entry_res in entries {
        let entry = entry_res.expect("读取目录项失败");
        let entry_path = entry.path();
        
        println!("Name: {}", entry_path.display());

        if entry_path.is_dir() {
            list_folder(&entry_path).await;
        }
    }
}

#[tokio::main]
async fn main() {
    let program_data = std::env::var("ProgramData").expect("无法获取ProgramData环境变量");
    let target_path = Path::new(&program_data).join("Microsoft/Windows/Start Menu/Programs");
    
    list_folder(&target_path).await;
}

关键说明

  • 仅对Result做一次unwrap操作,将结果保存到变量中,后续所有操作都引用这个变量,彻底避免所有权转移问题。
  • 使用Path::join拼接路径,比直接字符串拼接更安全,能自动处理Windows/Linux不同的路径分隔符。
  • 递归调用时传入子目录的引用,符合Rust的所有权规则,避免不必要的内存拷贝。

内容的提问来源于stack exchange,提问作者Ivanoio

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 20:30:42