Skip to content
CryoCryo home
StdlibSystem

fs

Filesystem access. File implements both Read and Write, so code already written against the I/O traits works against files with no glue.

ItemImport
Path, PathBufimport std::fs::path;
File, OpenOptions, read, write, copyimport std::fs::file;
read_dir, create_dir, remove_file, renameimport std::fs::dir;
Metadata, FileType, metadata, existsimport 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
MethodReturnsNotes
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)PathBufAppends 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\sharereplaces 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, ... */ }
}
ConstructorNotes
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 File wraps a raw fd, and the obligations are the POSIX ones: every open pairs with exactly one drop, which closes it. Calling drop twice is a bug the kernel may not catch — the second close could race an unrelated open that 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

FunctionNotes
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.

FunctionNotes
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.

MethodReturnsNotes
file_type()FileType
is_file() / is_dir() / is_symlink()boolean
len()u64Size in bytes, meaningful for regular files.
mode()u32Full st_mode — type bits plus permission bits.
permissions()u32The 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.