Ruff is a small programming language and runtime implemented in Rust. It is built for local scripting, automation, runtime experiments, and benchmarking work where a compact language with a Rust-hosted standard library is useful.
The project is currently at 0.14.0 in Cargo.toml, after completing the stabilization and v1-runway release cycle. Next planned roadmap work targets 1.0.0 scope execution and compatibility hardening. Ruff is usable from source today, but the language and runtime APIs are still evolving. Treat the repository tests, examples, and native-function dispatch tests as the source of truth for current behavior.
- Ruff can be installed from tagged release artifacts or built from source with Cargo.
ruff runuses the bytecode VM by default.ruff run --interpreterruns the tree-walking interpreter fallback.- The runtime includes a broad native standard library for strings, collections, files, data formats, HTTP, databases, crypto, process/system helpers, concurrency, and network primitives.
- Optional type annotations are parsed. In the CLI, type-checking warnings are emitted on the interpreter path; VM execution does not currently enforce a static type gate before running.
- Some advanced language surfaces are experimental or have runtime-mode gaps. See Known Boundaries.
- Ruff is not yet ready for a
1.0.0release. - ROADMAP.md is the single source of truth for release readiness and blocker tracking.
- Ruff
1.0.0must not be released until all P0/P1 roadmap items and the final release checklist are complete.
Ruff language tooling is being aligned around a universal-first architecture:
- shared language/tooling contracts
- one canonical Ruff LSP server for editor intelligence
- deterministic machine-readable CLI/LSP outputs
- shared grammar path for syntax highlighting
- thin editor adapters that launch/configure Ruff tooling instead of reimplementing it
Execution details and phased acceptance criteria are tracked in ROADMAP.md under the active v1.0.0 planning track.
The versioned language and compatibility contract baseline for this cycle is published in docs/LANGUAGE_SPEC.md.
Ruff can be installed from release artifacts or by building this repository.
git clone https://github.com/rufflang/ruff.git
cd ruff
cargo build --release
./target/release/ruff --versionFor development, use the debug build through Cargo:
cargo run -- --help
cargo run -- run examples/hello.ruffTo install the local checkout into Cargo's binary directory:
cargo install --path .
ruff --versionSee INSTALLATION.md for platform notes and troubleshooting.
Create hello.ruff:
func total(values) {
let sum := 0
for value in values {
sum := sum + value
}
return sum
}
let scores := [8, 13, 21]
let report := {"name": "build", "total": total(scores)}
if report["total"] > 40 {
print("ok: " + report["name"] + " = " + to_string(report["total"]))
} else {
print("too low")
}
Run it:
ruff run hello.ruffExpected output:
ok: build = 42
The current CLI exposes these subcommands:
| Command | Purpose |
|---|---|
ruff run <file> |
Run a .ruff script with the default VM (--scheduler-timeout-ms can override cooperative scheduler timeout); parse diagnostics (including source-size/depth limits) exit non-zero before execution. Native host-effect capability policy can be scoped with --untrusted plus --allow-* flags (--allow-fs-read, --allow-fs-write, --allow-fs-delete, --allow-process-exec, --allow-shell-exec, --allow-env-read, --allow-env-write, --allow-net-client, --allow-net-server, --allow-net, --allow-database, --allow-clock, --allow-random, or --allow-all). |
ruff run --interpreter <file> |
Run a .ruff script with the tree-walking interpreter. |
ruff serve [dir] |
Serve a directory over HTTP/HTTPS for local preview/testing (--host, --port, --index, --hardened, --cache-max-age, --access-log, --tls-cert, --tls-key). |
ruff repl |
Start the interactive REPL (tab completion, command highlighting, multiline continuation validation, and .help <function> support). |
ruff format <file> |
Format Ruff source files with opinionated defaults (--indent, --line-length, --no-sort-imports, --check, --write, --json). |
ruff lint <file> |
Lint Ruff source files for common issues (--fix for safe autofixes, --json for structured output). |
ruff init |
Initialize a Ruff project with ruff.toml and src/main.ruff. |
ruff package-add <name> |
Add a dependency to ruff.toml (--version, --manifest). |
ruff package-install |
Validate and enumerate dependencies declared in ruff.toml. |
ruff package-publish |
Preview or execute package publish metadata flow from ruff.toml. |
ruff docgen <file> |
Generate HTML docs from /// comments (--out-dir, --no-builtins, --json). |
ruff lsp |
Run the official Ruff LSP server over stdio JSON-RPC (--deterministic-logs for reproducible stderr tracing). |
ruff test |
Run .ruff files under tests/; --update regenerates expected-output snapshots. |
ruff test-run <file> |
Run tests declared with Ruff's test "name" { ... } syntax; parse diagnostics exit non-zero before test collection. Supports the same --untrusted / --allow-* native capability policy flags as ruff run. |
ruff bench [path] |
Run benchmark scripts. |
ruff bench-cross |
Compare Ruff parallel_map against a Python ProcessPoolExecutor benchmark. |
ruff bench-ssg |
Run the async SSG benchmark, with optional Python comparison and measurement controls. |
ruff profile <file> |
Profile a Ruff script for CPU, memory, and JIT stats. |
ruff lsp-complete <file> --line <N> --column <N> |
Return completion candidates (builtins, functions, variables) for editor/LSP integration; add --json for structured output. |
ruff lsp-definition <file> --line <N> --column <N> |
Return the go-to-definition location for the identifier under the cursor; add --json for structured output. |
ruff lsp-references <file> --line <N> --column <N> |
Return all references for the identifier under the cursor; add --include-definition false to exclude declarations and --json for structured output. |
ruff lsp-hover <file> --line <N> --column <N> |
Return hover details for the identifier under the cursor (kind, detail text, definition location when applicable); add --json for structured output. |
ruff lsp-diagnostics <file> |
Return source diagnostics for editor refresh loops (lexer failures, delimiter mismatches, and parser diagnostics); --json emits stable code and subsystem metadata per diagnostic. |
ruff lsp-rename <file> --line <N> --column <N> --new-name <NAME> |
Return rename edits for the symbol under the cursor and the updated source text; add --json for structured output. |
ruff lsp-code-actions <file> |
Return syntax quick-fix actions derived from diagnostics (for example unmatched/unclosed delimiters); add --json for structured output. |
ruff serve is intended to be a universal local static preview surface for Ruff users.
Examples:
# Basic HTTP preview
ruff serve output --host 127.0.0.1 --port 8080
# Hardened mode with access logs and explicit cache controls
ruff serve output --hardened --access-log --cache-max-age 300
# HTTPS preview (both certificate and key are required)
ruff serve output --tls-cert ./certs/dev-cert.pem --tls-key ./certs/dev-key.pemBehavior highlights:
- Supports
GETandHEADrequests (returns405for unsupported methods). - Enforces canonical root-boundary checks to block path traversal.
- Uses no-follow file reads on Unix to reduce symlink race/swap risks.
- Returns deterministic status mapping for common file errors (
404,403,500). - Adds ETag-based conditional responses (
304) and single-range byte serving (206/416). - Detects and serves precompressed sibling assets (
.br,.gz) when accepted. - Uses one centralized, case-insensitive MIME extension registry for known asset types.
- Serves unknown-extension and extensionless files as
application/octet-stream(including unknown active-content payloads). - Adds baseline hardening headers (
X-Content-Type-Options) and stricter security headers in hardened mode. - Adds
Strict-Transport-Securityonly for secure (TLS) requests.
Machine-readable output and automation contracts are documented in docs/CLI_MACHINE_READABLE_CONTRACTS.md.
Cross-editor thin-adapter setup baselines are documented in docs/EDITOR_ADAPTER_BASELINES.md.
Tree-sitter grammar assets and integration baseline are documented in docs/TREE_SITTER_RUFF.md.
Protocol contracts, install/upgrade guidance, and release artifact checklist docs:
- docs/PROTOCOL_CONTRACTS.md
- docs/INSTALLATION_LSP_EDITORS.md
- docs/RELEASE_ARTIFACT_CHECKLIST_V0_14_0.md
- docs/RELEASE_ARTIFACT_CHECKLIST_V0_13_0.md
- docs/RELEASE_PROCESS.md
- docs/RELEASE_ARTIFACT_VALIDATION.md
- docs/LSP_RELIABILITY.md
- docs/V1_SCOPE.md
Useful environment variables:
| Variable | Effect |
|---|---|
DISABLE_JIT=1 |
Disables JIT support in the VM path. |
DEBUG_AST=1 |
Prints the parsed AST before VM execution. |
RUFF_SCHEDULER_TIMEOUT_MS=<ms> |
Overrides the VM cooperative scheduler completion timeout when ruff run --scheduler-timeout-ms is not provided. The default is 120000. |
Scheduler timeout precedence for ruff run is:
--scheduler-timeout-ms <ms>RUFF_SCHEDULER_TIMEOUT_MS=<ms>- default
120000 ms
The v0.14.0 release delivered stabilization and release-hardening work required before v1.0.0:
- release process hardening with deterministic playbook + CI release-state guard
- fixture-locked LSP protocol stability guarantees and compatibility table
- packaging/distribution follow-through with Linux/macOS artifact validation and reproducible checksum flow
- tree-sitter and editor adapter maturity follow-through, including first-party extension smoke coverage in CI
- runtime/tooling reliability track with malformed-sequence, lifecycle churn, bounded-state, and latency guardrail coverage
- explicit v1.0.0 scope definition and deferred post-1.0 backlog commitments
Detailed release evidence and checklist completion notes are documented under notes/.
The v0.13.0 release delivered the cross-IDE tooling baseline and made Ruff's LSP/server contracts a first-class surface:
ruff lspofficial JSON-RPC server entrypoint with lifecycle handling, deterministic logging, and shared analyzer wiring.- Full required LSP method parity for
v0.13.0including diagnostics, completion, hover, definition, references, rename, code actions, formatting/range-formatting, document symbols, and workspace symbols. - Deterministic machine-readable CLI contracts with
--jsoncoverage for format/lint/docgen and LSP helper commands. - Fixture-driven LSP conformance harness and external client smoke coverage.
- Baseline tree-sitter grammar assets and editor-adapter setup guidance for VS Code/Cursor, Neovim, and JetBrains.
Detailed release evidence and artifact validation are documented in docs/RELEASE_ARTIFACT_CHECKLIST_V0_13_0.md and the completion notes under notes/.
Ruff still includes the v0.12.0 tooling additions that remain part of the current release:
- opinionated spacing/indentation normalization
- configurable indentation width
- line-length-aware wrapping for comma-separated expressions
- leading import-block sorting (optionally disabled)
Ruff source files use the .ruff extension. The implemented syntax includes:
- Numeric, string, boolean,
null, array, and dictionary values. let,mut, andconstbindings with:=assignment syntax.letandconstbindings are immutable (reassignment and in-place mutation through those bindings are runtime errors);mutbindings allow reassignment and in-place mutation.- Duplicate declarations in the same lexical scope are runtime/compile errors (
Duplicate declaration in the same scope: <name>). - Assignment/update statements support
:=,=,+=,-=,*=,/=, and%=(chained assignment likea := b := 1is rejected). - Operator precedence is explicit and documented in
docs/LANGUAGE_SPEC.md(postfix/unary through multiplicative/additive/comparison/equality/boolean/null-coalescing/pipe). - Optional annotations on variables and functions, such as
let x: int := 42andfunc add(a: int, b: int) -> int. - Functions declared with
func, including anonymous function expressions andasync func. if/else,while,loop,for ... in,break, andcontinue.breakandcontinueare only valid inside loop bodies; using either outside a loop is a deterministic runtime/compile error.- Top-level
returnremains supported as the script-exit value contract, while functionreturnkeeps standard early-exit behavior. - Function body fallthrough and
returnwithout an expression both yieldnull; explicitreturn 0remains integer zero. - Function bodies and control-flow bodies (
if/else,while,loop, andfor) use lexical scopes; inner shadowing is allowed, loop variables do not leak after loop completion, and closures capture the nearest lexical binding. - Shared truthiness semantics across interpreter/VM/native predicates:
false,null,0,0.0, empty string, empty array, and empty dictionary are falsey; all other values are truthy. - Logical
&&and||use truthiness and short-circuit (RHS is evaluated only when required), returning boolean results. - Integer arithmetic (
+,-,*,/,%) is checked at runtime (i64): overflow and divide/modulo-by-zero return deterministic runtime errors. - Float division/modulo by zero returns deterministic runtime errors; NaN and infinity comparisons follow documented language-spec behavior.
- Equality/comparison semantics are explicit across interpreter and VM:
1 == 1.0is true, arrays/dictionaries compare deeply by value, callable equality is identity-based, and unsupported ordering pairs (for examplebool < bool) return deterministic runtime errors. - Arrays and dictionaries with indexing and standard library helpers.
- Undefined identifiers are runtime errors. Use quoted string literals when a string value is intended.
- Missing dictionary keys are runtime errors. Use helpers such as
has_key,get, orget_defaultwhen a fallback value is intended. - Out-of-bounds array/string indexing, indexing non-indexable values, and invalid index-assignment targets are runtime errors.
- Unsupported unary/binary operations are runtime errors (no implicit
Int(0)or empty-string fallback). - Function/method/native call arity is enforced at runtime. Too few or too many arguments return deterministic errors that include callable name plus expected and received counts/ranges.
- String interpolation with
${...}inside double-quoted strings. ResultandOptionvalues throughOk(...),Err(...),Some(...), andNone.match/casestatements, including parity-coveredResult::/Option::tag-style bindings (Ok(value),Err(error),Some(value)) on both interpreter and VM paths.try/exceptblocks andthrow(...)for runtime error flow.awaitexpressions and promise-returning async/native operations.spawn { ... }syntax and native concurrency helpers.structdeclarations, field access, and struct instance literals.test,test_setup,test_teardown, andtest_groupdeclarations for the test runner.#,//,/* ... */, and///comments.- Lexing malformed source now fails with structured diagnostics (line/column anchored) instead of silently skipping invalid characters or coercing malformed numeric literals.
- Parsing malformed source now returns structured parser diagnostics with consistent source spans (start/end line+column and byte-offset bounds), including delimiter/EOF errors and invalid assignment targets; CLI parse failures exit non-zero before runtime execution.
- Parser safety limits are enforced for CLI parse entrypoints: source files over
1,048,576bytes, expression nesting deeper than256, or statement-block nesting deeper than128fail with structured parse diagnostics.
A small async example:
async func fetch_label(id) {
return "item-" + to_string(id)
}
let first := fetch_label(1)
let second := fetch_label(2)
print(await first)
print(await second)
Native functions are registered in src/interpreter/mod.rs and dispatched through src/interpreter/native_functions/. The current categories include:
- I/O and debugging:
print,input,debug, assertions. - Math:
abs,sqrt,pow,floor,ceil,round,min,max, trigonometry,PI,E. - Strings and regex: length, case conversion, trimming, splitting/joining, padding, slug/case helpers, regex match/find/replace/split.
- Collections: array helpers, higher-order functions, dictionaries, sets, queues, stacks, ranges.
- Type conversion and introspection:
parse_int,parse_float,to_string,to_bool,type,is_*helpers,bytes. - Filesystem and binary I/O: text files, binary files, metadata, random access reads/writes, copy/truncate helpers, path helpers, OS helpers.
- Data formats: JSON, TOML, YAML, CSV, and Base64.
- System utilities: environment variables, CLI args, time/date helpers, sleep, process execution, process spawning, command piping.
- HTTP and auth: HTTP client calls, streaming/binary HTTP helpers, response helpers, JWT helpers, OAuth2 helpers, parallel HTTP.
- Database helpers: SQLite, Postgres, MySQL-oriented connection/query APIs, pools, transactions, last-insert-id helpers.
- Async and concurrency: sleep/timeout promises, async file/HTTP operations, task handles,
Promise.allaliases,parallel_map, shared state, channels, task-pool sizing. - Media/archive/crypto/network: image loading, zip/unzip, SHA/MD5, bcrypt, AES, RSA, TCP, and UDP helpers.
The standard library is broad, but not all functions have polished reference documentation yet. When adding user-facing docs for a specific API, verify the handler contract in src/interpreter/native_functions/ and the corresponding regression tests.
High-risk native APIs (process/network/filesystem/crypto/database) are powerful. ruff run and ruff test-run default to trusted capability mode for local workflows, and can be switched to deny-by-default mode with --untrusted plus explicit --allow-* capability flags. Review docs/NATIVE_API_SECURITY_POSTURE.md before running untrusted scripts or deploying automation in shared environments.
For archive extraction, unzip(...) now rejects unsafe entry paths (absolute paths, .. traversal components, drive-prefixed names, null-byte names, and symlink entries) and enforces extraction limits (1024 entries, 16 MiB per entry, 64 MiB total uncompressed size).
Ruff has two testing layers.
Run the Rust test suite:
cargo testRun the release-gate CI suite locally:
bash scripts/release_gate.shFast smoke mode (useful before pushing, and the mode CI uses for lightweight script-validation):
bash scripts/release_gate.sh --minimalTo include socket-bound static-serve integration tests in local gate runs:
RUFF_ENABLE_SOCKET_TESTS=1 bash scripts/release_gate.shRelease-gate prerequisites and runtime profile:
- Requires Rust toolchain with
cargo fmt,cargo clippy, andcargo testavailable. - Optionally runs
cargo auditandcargo deny checkin full mode when those tools are installed. - Optional benchmark smoke can be enabled in full mode with
RUFF_RELEASE_GATE_RUN_BENCH=1. - Typical runtime:
--minimal: usually under a few minutes.- full mode: usually several minutes and longer on busy machines.
Run the async runtime concurrency stability regression directly:
cargo test test_concurrent_tasksRun repository .ruff snapshots:
cargo run -- test
cargo run -- test --updateRun Ruff tests declared in a specific file:
cargo run -- test-run tests/testing_framework.ruffExample Ruff test file:
test "array length" {
let values := [1, 2, 3]
assert_equal(len(values), 3)
}
test "string predicate" {
assert_true(starts_with("ruff", "ru"))
}
Ruff includes benchmark tooling because performance work is a first-class part of the project, especially for the VM, async scheduler, and native SSG benchmark path.
cargo run -- bench examples/benchmarks
cargo run -- bench-cross
cargo run -- bench-ssg --runs 5 --warmup-runs 1
cargo run -- bench-ssg --compare-python --profile-async
cargo run -- profile examples/benchmark.ruffbench-ssg supports repeated runs, warmups, percentile reporting, measurement-quality warnings, optional Python comparison, optional stage profiling, and a throughput gate via --throughput-gate-ms.
These are intentional caveats for production readers rather than fine print:
- Tagged releases publish standalone Linux/macOS artifacts with checksums; package-manager taps remain outside the current v1 release gate.
- VM execution is the default, but the tree-walking interpreter still matters for some language surfaces and diagnostics.
- Static typing is optional and not a VM-enforced compile-time contract in the current CLI path.
import/exportsyntax and module execution/export collection are implemented, and end-to-end package/module workflow integration coverage is present for core CLI flows.- Struct fields and instance literals exist, and explicit
selfstruct methods are parity-covered in VM/interpreter tests. - Struct generator methods (
func*insidestruct) are intentionally unsupported and fail with a deterministic error (Generator methods are not supported for structs: <Struct>.<method>). - Spread literals and destructuring syntax are parity-covered in VM/interpreter tests for the tracked matrix scenarios.
spawn { ... }is parsed and executed as fire-and-forget work; do not rely on spawned output or shared state without using the explicit concurrency/native async APIs.Result/Optionvalues are implemented, and tag-style match bindings are parity-covered on both VM and interpreter paths for the tracked scenarios.- Benchmark numbers are environment-sensitive. Prefer
bench-ssg --runs <N> --warmup-runs <N>and read the measurement warnings before drawing conclusions. - Runtime parity status is tracked in docs/VM_INTERPRETER_PARITY_MATRIX.md.
- INSTALLATION.md: build and installation notes.
- CHANGELOG.md: release and unreleased changes.
- ROADMAP.md: planned work.
- CONTRIBUTING.md: contribution workflow.
- docs/CONCURRENCY.md: async/concurrency model notes.
- docs/PERFORMANCE.md: benchmark and profiling notes.
- docs/VM_INSTRUCTIONS.md: bytecode VM instruction reference.
- docs/EXTENDING.md: adding native functionality.
- docs/DEPRECATION_POLICY.md: semver-tied deprecation windows and removal policy.
- docs/NATIVE_API_SECURITY_POSTURE.md: trust model and operational caveats for high-risk native APIs.
Some older docs may lag the current VM-default execution path. Prefer the code and tests when behavior differs.
Ruff is licensed under the MIT License. See LICENSE.