Memory-safe systems code,without the ceremony.
Ownership frees memory and rules out data races, with no lifetimes to annotate. Errors are values, every task lives in a scope, and it all compiles fast to C, then to one native binary.
- Garbage collector
- None
- Null pointers
- None
- Lifetime annotations
- None
- Compiles to, then native
- C
use std/os
struct Config {
name: String // required
port: int = 8080 // default value
proxy: ?String // optional, defaults to none
}
fn load(path: str) -> Config ! os.Error {
let text = try os.readText(path)
Config{name: text.trim().toString()}
}
fn main() {
let cfg = load("app.toml") catch e {
eprintln("using defaults:", e)
.{name: "app"}
}
let backups = [8081, 80, 9000]
.filter(p => p > 1024)
.map(p => ":${p}")
println("${cfg.name} on :${cfg.port}, backups ${backups.join(", ")}, proxy: ${cfg.proxy ?? "none"}")
}Structs with defaults, errors as values, optional fields and string interpolation, from the README.
Read it in the tourOwnership without lifetimes
Memory is freed by ownership, as in Rust, but references can't be stored, only used, so the compiler checks them without annotations.
Errors are values
A function that can fail returns T ! E. try passes the error up, catch handles it, errdefer cleans up; none can be ignored by accident.
Structured concurrency
go, channels and select, made structured: every task belongs to a scope that waits for it, with cancellation down the tree. Data races don't compile.
Generics and interfaces
Generic types and functions with bounds, and interfaces a type satisfies by having the methods, without declaring it.
Tools in one binary
gojo run, build, test, fmt and doc, with the standard library and the runtime compiled in. Cross-compile static Linux binaries with zig.
Made for coding agents
Diagnostics say what to write instead, gojo doc prints any module's API, and these docs are one file away at /llms.txt.