Skip to content

std.fs

API summary (generated from the Beans source by npm run coverage): 15 package functions.

std.fs gives you one-call helpers to read or write a whole file. Under the hood it opens a File, does the work, and closes it. Read the source at stdlib/std/fs/fs.b.

Every function returns a Result, because filesystem work can fail. Use ? to pass the error up.

import std.fs
pub fn read_bytes(path: string) -> Result<Bytes>
pub fn read(path: string) -> Result<string>
  • read_bytes opens the file, reads it whole from offset 0, and returns the bytes.
  • read does the same and fills its returned string directly. It does not build an intermediate Bytes value.
pub fn write_bytes(path: string, data: Bytes) -> Result<int>
pub fn write(path: string, data: string) -> Result<int>
pub fn append_bytes(path: string, data: Bytes) -> Result<int>
pub fn append(path: string, data: string) -> Result<int>
pub fn copy(from: string, to: string) -> Result<int>

All five return the number of bytes written.

  • write_bytes and write open the file in “create” mode, truncate it to empty, and write starting at position 0. write takes a string; write_bytes takes Bytes. Text writes use the string storage directly.
  • append_bytes and append open the file in “append” mode and add data to the end instead of truncating.
  • copy uses the platform file-copy path when available and a fixed-size fallback. It does not hold the whole source file in a Beans buffer. A same-file or hard-link copy fails before the destination can be truncated.
import std.io
import std.fs
fn main() {
fs.write("greeting.txt", "hello\n").expect("write")
fs.append("greeting.txt", "again\n").expect("append")
let text: string = fs.read("greeting.txt").expect("read")
io.print(text) // hello / again
}
pub fn write_durable(path: string, data: string) -> Result<int>
pub fn write_bytes_durable(path: string, data: Bytes) -> Result<int>
pub fn sync(path: string) -> Result<bool>

write_durable and write_bytes_durable truncate, write, flush and close on the same handle, returning the byte count. Empty input still truncates and flushes. They add one OS flush to an ordinary write and do not reopen the file.

sync opens an existing file in "rw" mode, flushes and closes it without creating or truncating it. It requires read/write permission on every platform for the portable Windows contract. It adds one open, one flush and one close.

The helpers propagate open, write, flush and successful-path close errors. Cleanup after an earlier error preserves that error. A failed write may leave the file changed; there is no rollback.

These calls request fsync on POSIX or FlushFileBuffers on Windows, subject to filesystem and device guarantees. They flush file contents; directory entries remain the responsibility of Dir.sync. Writes truncate in place.

For atomic replacement, write a distinct temporary file in the destination directory using write_durable, rename it over the destination, then call Dir.sync on the parent directory. Sync both directories for a move between directories. Coordinate concurrent writers yourself. Directory sync on Windows is currently best effort, so success does not prove metadata durability.

Directories stay on Dir: use create, create_all, list, walk, remove, remove_all, exists, current, temp_path and sync directly.

pub fn exists(path: string) -> bool
pub fn size(path: string) -> Result<int>
  • exists answers yes or no and never fails: a path you cannot reach is a path that is not there, as far as this call is concerned.
  • size is the file’s length in bytes without reading it.
pub fn rename(from: string, to: string) -> Result<bool>
pub fn remove(path: string) -> Result<bool>
pub fn temp_dir() -> string
  • rename moves a file atomically within a filesystem, replacing to if it is there. It can fail across filesystems and does not fall back to copying.
  • remove answers ok(true) when it deleted the file and ok(false) when there was nothing to delete. Only a real failure — a permission, a directory in the way — comes back as err, so “make sure this is gone” needs no exists check first.
  • temp_dir is the directory the platform hands out for scratch files. It is a path, not a file: create your own name under it, and clean up after yourself.
import std.io
import std.fs
import std.path
fn main() {
let scratch: string = path.join(fs.temp_dir(), "notes.txt")
fs.write(scratch, "draft\n").expect("write")
io.println(fs.size(scratch).expect("size")) // 6
fs.remove(scratch).expect("remove")
io.println(fs.exists(scratch)) // false
}
  • Files and mapping, the File type for positional and cursor I/O when you need finer control.
  • std.path, build the path strings you pass here.
  • std.reader, read a file line by line instead of all at once.