Bun's unreleased Rust port has 13,365 unsafe blocks. Most can be removed.
Where it comes from
Every site by root cause. Three — performance, the Zig port, the FFI boundary — cover two-thirds.
Compared to other Rust runtimes
Unsafe per 1,000 lines of Rust, measured the same way in each project. Density tracks how close the code sits<br>to a C boundary: a crate that only binds a C++ engine is densest, a runtime that writes its own engine in Rust<br>is sparsest. Bun keeps its bindings and runtime in one workspace; Deno splits the binding into rusty_v8 and<br>writes much of its runtime in TypeScript. Draw your own conclusions.
Counted with the measurement command from the<br>methodology section, vendored C/C++ excluded, May 21 2026, commits pinned per row. Lines are Rust source only.
Where it lives
Area = unsafe sites. Greener = more of them replaceable. Click a tile for its breakdown.
fewer sound destinationsmore<br>area ∝ sites · Σ =
Tap a subsystem tile above to drill in.
Origin → pattern → outcome
Root cause → pattern → outcome, for every site. Click a node to trace it. The Zig-port and<br>callback unsafe mostly ends up green (replaceable); the FFI unsafe mostly ends up blue (stays, behind a<br>wrapper).
← scroll →
Click an origin, pattern, or destination to trace where its sites flow.
What the count is made of
Six facts about the 13,365 that the raw number doesn't carry.
15 patterns, five outcomes
A site counts as fixed only if safe code can't cause undefined behavior in a release<br>build. Debug assertions don't qualify; neither do wrappers that just hide the invariant. Everything else stays<br>unsafe — the blue and gray. The four largest patterns were measured per site; the rest are estimates<br>over verified counts. Expand a row for examples and the fix.
Σ = 13,365 · sorted by size · the bar is each pattern's outcome splitCollapse all
Three questions, answered per site
Three questions determine most of the ledger, and each one means reading the site. Two<br>classifiers answered independently; an adjudicator settled every disagreement against the code. Where agreement<br>was low, trust the number less. Each pass samples by its own membership rule — the first read 3,531 sites, of<br>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<br>consume the ledger cells.
Soundness first, then the count
Eight steps. The first fixes the safe functions that are wrong today — no change to the count. The rest move<br>~9,300 sites to safe code and leave ~4,000 unsafe. Per-pattern figures are in the ledger above.
← scroll →
How this was measured, and what to be skeptical of
A snapshot of the Rust port at commit 3eb0fda021, before it has shipped in a<br>release — counts and line numbers move as the code does. The ground truth is one ripgrep command<br>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<br>both independent classifiers plus the adjudicated final (33 × 3 files) · the flagged safe-function list with<br>claimed trigger sequences · the synthesis documents this page renders.