unsafe blocks.Bun's Rust port has not shipped in a released build yet. The Bun you install today still runs the original Zig implementation. This audit is the pre-release pass over the port.
Every unsafe counted with one ripgrep command, then sorted by what
removing it takes — command and raw data at the bottom, re-run it yourself. The bar groups all 13,365 by where
each could go.
Every site by root cause. Three — performance, the Zig port, the FFI boundary — cover two-thirds.
Unsafe per 1,000 lines of Rust, measured the same way in each project. Density tracks how close the code sits to a C boundary: a crate that only binds a C++ engine is densest, a runtime that writes its own engine in Rust is sparsest. Bun keeps its bindings and runtime in one workspace; Deno splits the binding into rusty_v8 and writes much of its runtime in TypeScript. Draw your own conclusions.
Counted with the measurement command from the methodology section, vendored C/C++ excluded, May 21 2026, commits pinned per row. Lines are Rust source only.
Area = unsafe sites. Greener = more of them replaceable. Click a tile for its breakdown.
Root cause → pattern → outcome, for every site. Click a node to trace it. The Zig-port and callback unsafe mostly ends up green (replaceable); the FFI unsafe mostly ends up blue (stays, behind a wrapper).
Six facts about the 13,365 that the raw number doesn't carry.
A site counts as fixed only if safe code can't cause undefined behavior in a release build. Debug assertions don't qualify; neither do wrappers that just hide the invariant. Everything else stays unsafe — the blue and gray. The four largest patterns were measured per site; the rest are estimates over verified counts. Expand a row for examples and the fix.
Three questions determine most of the ledger, and each one means reading the site. Two classifiers answered independently; an adjudicator settled every disagreement against the code. Where agreement was low, trust the number less. Each pass samples by its own membership rule — the first read 3,531 sites, of which 2,750 are the two largest patterns in the ledger — raw *mut Self state machines and &self→&mut casts. The measured ratios apply to the ledger counts, and the steps consume the ledger cells.
Eight steps. The first fixes the safe functions that are wrong today — no change to the count. The rest move ~9,300 sites to safe code and leave ~4,000 unsafe. Per-pattern figures are in the ledger above.
A snapshot of the Rust port at commit 3eb0fda021, before it has shipped in a
release — counts and line numbers move as the code does. The ground truth is one ripgrep command
anyone can re-run, and everything above reconciles to it.
Measurement command:
Raw data: per-group census reports and adversarial reviews (102 + 102 files) · per-site classifications from
both independent classifiers plus the adjudicated final (33 × 3 files) · the flagged safe-function list with
claimed trigger sequences · the synthesis documents this page renders.