errno

Cross-platform Rust access to the OS errno variable, with human-readable error descriptions on Unix, Windows, WASI, and Hermit.

Library
Cargo
v0.3.14
77stars
MIT OR Apache-2.0

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
54/100Fair
Development Activity56
Maintenance36
Community52
Maturity60
Momentum12

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
63/100Good
Architecture78
Code Quality68
Innovation72
Learning Curve35

errno is a small, no_std-compatible Rust crate that provides a uniform interface to the operating system’s errno value across Unix, Windows, WASI, and Hermit targets. It exposes a single Errno type that wraps the platform-specific integer error code and implements Display by calling into the OS’s native error-formatting routine — strerror_r on POSIX systems and FormatMessageW on Windows — so callers get a human-readable message without writing platform-specific code themselves.

The crate is deliberately minimal: it performs no heap allocations, supports #![no_std] environments when the std feature is disabled, and integrates with the standard library’s io::Error and Error traits when std is enabled. Because it underlies error handling in many lower-level systems crates via libc on Unix-like platforms and windows-sys on Windows, errno functions as foundational plumbing rather than an application-facing library.

What You Get

  • A single Errno(i32) newtype wrapping the raw OS error code, with Debug, Display, Eq, Ord, and Hash implementations built in
  • errno() and set_errno() free functions that read and write the platform’s thread-local error state
  • Automatic, platform-correct error messages via strerror_r on Unix/WASI/Hermit and FormatMessageW on Windows
  • #![no_std] support when the default std feature is disabled, with zero heap allocations in the description-lookup path
  • A From<Errno> for std::io::Error conversion so OS errors slot directly into idiomatic Rust error handling when std is enabled

Common Use Cases

  • Wrapping raw libc/syscall return values in low-level bindings so callers get a typed, printable error instead of a bare integer
  • Building portable systems libraries (file I/O, networking, process management) that need one error type across Unix, Windows, WASI, and embedded (Hermit) targets
  • Converting OS-level failures into std::io::Error at the boundary of a no_std or std-optional crate
  • Diagnosing native library or FFI failures where the underlying C API only reports errors through errno

Under The Hood

Architecture The crate follows a strict platform-strategy pattern centered on lib.rs, which conditionally re-exports one of four backend modules — unix.rs, windows.rs, wasi.rs, or hermit.rs — into a common sys module via #[cfg_attr(target, path=…)] attributes, falling back to a stub sys.rs that emits a compile_error! for genuinely unsupported targets rather than failing silently. Every backend implements the same four-item contract (errno(), set_errno(), with_description(), and a STRERROR_NAME constant), so the public Errno newtype in lib.rs never branches on platform itself — it simply calls into whichever sys module was selected at compile time. Unix’s implementation resolves the correct errno_location() symbol per-OS through a dense table of #[cfg_attr(target_os = …, link_name = …)] attributes (covering Linux, the BSDs, Android, Solaris/illumos, Haiku, AIX, and more) and formats messages via strerror_r into a fixed 1024-byte stack buffer; Windows instead calls GetLastError/SetLastError and formats via FormatMessageW into a 2048-entry UTF-16 buffer decoded manually with a hand-rolled from_utf16_lossy. Because each backend is self-contained and the public surface never grows, adding a new target is a matter of writing one new backend file and one cfg_attr line — the core abstraction would only break if a future target needed more than an i32 to represent its error state.

Tech Stack errno is a minimal-dependency Rust library (edition 2018, MSRV 1.56) built entirely on core plus two conditional external crates: libc (default-features disabled) on Unix/WASI/Hermit for strerror_r and the platform errno_location symbol, and windows-sys (pinned >=0.52,<0.62) on Windows for the raw Win32 FormatMessageW/GetLastError/SetLastError bindings — no higher-level windows crate, just the sys-level bindings. The default std feature gates the std::error::Error and From<Errno> for std::io::Error impls; disabling it makes the crate #![no_std]-compatible for embedded or WASI-without-std targets. There is no build script, no proc-macro, and no runtime dependency beyond these two FFI layers; CI and Dependabot config are the only supporting tooling, and the crate ships with no additional build configuration.

Code Quality Testing lives entirely inline in lib.rs as #[test] functions rather than a separate tests directory — a reasonable choice given the crate’s tiny surface, though the description-check test hand-branches its expected strings per target_os (Windows, illumos, WASI/emscripten, Haiku, vxworks, default), showing active tracking of platform-specific string differences rather than skipping them. Error handling favors explicit Result<&str, Errno> returns over panicking, and unsafe blocks are narrowly scoped around the specific FFI call rather than wrapping large regions. There’s no dedicated linter config beyond a clippy.toml exemption entry and #[clippy::msrv] annotations, and no rustfmt.toml, but CI runs on every push, and naming is consistent and idiomatic Rust throughout. Type safety is strong — the crate deliberately avoids String/heap allocation, using fixed-size stack buffers and lossy UTF-8/UTF-16 decoding helpers instead.

API Design errno’s contribution isn’t a novel algorithm but disciplined scope: it exposes exactly the errno primitive that std::io::Error::last_os_error() uses internally, without pulling in the rest of std::io, so no_std and embedded consumers get OS error introspection they otherwise couldn’t. Getting started requires no configuration beyond the dependency line — call errno() or set_errno() and the crate handles platform dispatch invisibly. Its per-target link_name table for locating the platform’s errno symbol is more exhaustive than most hand-rolled FFI code bothers to be, and the crate has become de facto plumbing for the broader Rust systems ecosystem (rustix and similar low-level crates depend on it) precisely because it solves this one narrow problem completely rather than bundling it into a larger I/O abstraction.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search