fs
Filesystem access. File implements both Read and Write, so code already written against the I/O traits works against files with no glue.
| Item | Import |
|---|---|
Path, PathBuf | import std::fs::path; |
File, OpenOptions, read, write, copy | import std::fs::file; |
read_dir, create_dir, remove_file, rename | import std::fs::dir; |
Metadata, FileType, metadata, exists | import std::fs::metadata; |
Path and PathBuf
Both are length-typed UTF-8 byte sequences with no NUL terminator. Path borrows; PathBuf owns. Crossing into libc goes through a NUL-terminated String built with String::with_null.
Path syntax is per-target. On Unix / separates and a leading / means absolute — the whole path is one flat sequence of segments. On Windows both / and \ separate, and a path may open with a root that is not a segment and must never be split off or reordered:
C:\dir\file drive-absolute root `C:\`
C:dir drive-relative root `C:`, resolved against that drive's own cwd
\\server\share UNC
| Method | Returns | Notes |
|---|---|---|
static from(source: Str) | Path | |
as_str() / length() / is_empty() | ||
is_absolute() | boolean | |
file_name() | Option<Str> | The last segment. None for a trailing separator, an empty path, or a bare root. Does not consult the filesystem. |
parent() | Option<Path> | None for a single relative segment, a bare root, or empty. A rooted path yields the root itself, never "". |
extension() | Option<Str> | See below. |
join(segment: Str) | PathBuf | Appends to a copy, leaving the borrowed path untouched. |
extension() consults only the file name, so a dot in a directory component cannot leak in:
foo.rs -> Some("rs")
foo.tar.gz -> Some("gz") last dot only
foo. -> Some("") trailing dot, empty extension
foo -> None no dot
.bashrc -> None a leading dot is not an extension
.. -> None
a/b -> None ends in a directory
A leading dot only disqualifies when it is the only dot — .foo.rs still has extension rs.
PathBuf adds new(), from(source: Str), as_path(), push(segment: Str), pop() -> boolean, and into_raw(). push and join share their root semantics: a segment carrying its own root — /etc, C:\dir, \\server\share — replaces rather than extends.
File
import std::fs::file;
import std::fs::path;
match (File::open(Path::from(Str::new("config.json")))) {
Result::Ok(f) => { /* f is Read + Write + Seek */ }
Result::Err(e) => { /* NotFound, PermissionDenied, ... */ }
}
| Constructor | Notes |
|---|---|
static open(p: Path) | Open for reading. |
static create(p: Path) | Create or truncate for writing. |
static open_with(p: Path, options: &OpenOptions) | Full control. |
File implements Read, Write, and Seek, plus the inherent seek_set, seek_cur, seek_end, and stream_position helpers for direct use.
A
Filewraps a raw fd, and the obligations are the POSIX ones: every open pairs with exactly one drop, which closes it. Callingdroptwice is a bug the kernel may not catch — the secondclosecould race an unrelatedopenthat reused the descriptor.
OpenOptions
A builder. read, write, append, truncate, create, and create_new each take a boolean and return the options, so they chain; open(p) finishes.
mut opts: OpenOptions = OpenOptions::new();
mut log: File = opts.write(true).create(true).append(true).open(p)?;
Whole-file helpers
| Function | Notes |
|---|---|
read(p: Path) | Result<Array<u8>, IoError> |
read_to_string(p: Path) | Result<String, IoError> — bytes taken verbatim, no UTF-8 validation. |
write(p: Path, bytes: Slice<u8>) | Creates or truncates. |
copy(from: Path, to: Path) | Result<u64, IoError> — bytes copied. |
copy creates to if missing, truncates it if present, then applies the source's Unix permission bits, mirroring cp. The source must be a regular file — copying a directory fails with InvalidInput. On a mid-copy failure the partially written destination is left in place and the error is returned as-is.
Directories
read_dir(p: Path) -> Result<ReadDir, IoError> opens a directory for iteration. ReadDir is a forward iterator yielding owned DirEntry values and skipping . and ..; order is filesystem-defined, and dropping it closes the handle.
import std::fs::dir;
mut entries: ReadDir = read_dir(p)?;
for (e in entries) {
if (e.is_file()) { println(f"{e.name()}"); }
}
DirEntry owns its name and exposes name() -> Str, file_type() -> FileType, is_dir(), and is_file(). The type comes from the dirent d_type field, which some filesystems report as unknown — call metadata on the full path when you need an authoritative answer.
| Function | Notes |
|---|---|
create_dir(p: Path) | One directory, mode 0755. Fails if it exists or a parent is missing. |
create_dir_all(p: Path) | mkdir -p. Succeeds if p already exists as a directory, and tolerates a concurrent creator racing on the same path. |
remove_file(p: Path) | unlink(2) — files and symlinks. |
remove_dir(p: Path) | rmdir(2) — must be empty. |
remove_dir_all(p: Path) | rm -r. Symlinked entries are unlinked, not followed. Returns the first failure. |
rename(from: Path, to: Path) | rename(2) — atomic within a filesystem, EXDEV across one. |
canonicalize(p: Path) | Result<PathBuf, IoError> |
Metadata
metadata(p) follows symlinks like stat(2); symlink_metadata(p) reports the link itself like lstat(2). Both return Result<Metadata, IoError>. The predicates exists(p), is_file(p), and is_dir(p) return a bare boolean.
Metadata is a POD snapshot — freely copied, nothing to drop.
| Method | Returns | Notes |
|---|---|---|
file_type() | FileType | |
is_file() / is_dir() / is_symlink() | boolean | |
len() | u64 | Size in bytes, meaningful for regular files. |
mode() | u32 | Full st_mode — type bits plus permission bits. |
permissions() | u32 | The permission bits alone. |
Platform notes
On POSIX this is stat(2) and lstat(2). struct stat is opaque across glibc versions, so it is read as a fixed-size buffer with the needed fields pulled out by their Linux x86-64 offsets.
On Windows it uses the Win32 file API rather than msvcrt's stat shims, which lay the struct out completely differently — decoding it with the POSIX offsets would return garbage. metadata opens an attribute-only handle so directories open too, and symlink_metadata reads the reparse point directly. Windows has no POSIX permission bits, so mode() is synthesized there: type bits from the file attributes plus 0644/0444 for files (by the read-only attribute) or 0755/0555 for directories. It is not a view of the real ACL.