From a2793e4daf0d5f350822e0882c06c8da8ee8fd4f Mon Sep 17 00:00:00 2001 From: Mahdi Ali-Raihan Date: Sat, 4 Apr 2026 16:08:16 -0400 Subject: [PATCH] Changed implementation of remove_dir_all from recursive to an iterative version --- library/std/src/sys/fs/common.rs | 54 ++++++++++++++++++++++---------- 1 file changed, 38 insertions(+), 16 deletions(-) diff --git a/library/std/src/sys/fs/common.rs b/library/std/src/sys/fs/common.rs index 4d47d392a8268..2ed50f707be3f 100644 --- a/library/std/src/sys/fs/common.rs +++ b/library/std/src/sys/fs/common.rs @@ -1,4 +1,5 @@ #![allow(dead_code)] // not used on all platforms +use alloc_crate::collections::VecDeque; use crate::io::{self, Error, ErrorKind}; use crate::path::{Path, PathBuf}; @@ -29,27 +30,48 @@ pub fn copy(from: &Path, to: &Path) -> io::Result { pub fn remove_dir_all(path: &Path) -> io::Result<()> { let filetype = fs::symlink_metadata(path)?.file_type(); - if filetype.is_symlink() { fs::remove_file(path) } else { remove_dir_all_recursive(path) } + if filetype.is_symlink() { fs::remove_file(path) } else { remove_dir_all_iterative(path) } } -fn remove_dir_all_recursive(path: &Path) -> io::Result<()> { - for child in fs::read_dir(path)? { - let result: io::Result<()> = try { - let child = child?; - if child.file_type()?.is_dir() { - remove_dir_all_recursive(&child.path())?; - } else { - fs::remove_file(&child.path())?; +fn remove_dir_all_iterative(path: &Path) -> io::Result<()> { + // In unix/windows/solid/hermit/motor/uefi implementation of ReadDir struct, there is a field + // called `root` that contains the directory path that we are reading directory entries + // from. This makes holding a PathBuf in this tuple redundant since we only need this to remove + // the directory when we have exhausted the ReadDir iterator; if we can expose that field + // in the ReadDir struct through a method that return &Path, we can reduce memory usage + // allocated to this VecDeque. + + let mut directories = VecDeque::new(); + directories.push_front((path.to_path_buf(), fs::read_dir(path)?)); + + while !directories.is_empty() { + let (parent_path, read_dir) = &mut directories[0]; + let child = read_dir.next(); + if let Some(child) = child { + let result: io::Result<()> = try { + let child = child?; + let child_path = child.path(); + if child.file_type()?.is_dir() { + let child_readdir = fs::read_dir(&child_path)?; + directories.push_front((child_path, child_readdir)); + } else { + fs::remove_file(&child_path)?; + } + }; + + // ignore internal NotFound errors to prevent race conditions + if let Err(err) = &result + && err.kind() != io::ErrorKind::NotFound + { + return result; } - }; - // ignore internal NotFound errors to prevent race conditions - if let Err(err) = &result - && err.kind() != io::ErrorKind::NotFound - { - return result; + } else { + ignore_notfound(fs::remove_dir(parent_path))?; + directories.pop_front(); } } - ignore_notfound(fs::remove_dir(path)) + + Ok(()) } pub fn exists(path: &Path) -> io::Result {