Yeda AI Tips · #132

Español

Every unsafe Block Needs a Safety Comment

unsafe without a comment is just "trust me bro" in Rust. The block compiles, the tests pass, and the one thing that keeps it sound — the invariant you checked in your head — is written down nowhere. A reviewer can't confirm it. An AI agent editing the file six months later can't confirm it. The next refactor quietly breaks the assumption and you get undefined behavior with no warning.

Why the comment carries the weight

An unsafe block doesn't disable safety. It moves the burden of proof from the compiler to you. Inside it you can dereference raw pointers, call other unsafe functions, or access a union field — operations where the compiler stops verifying the contract and trusts that you already did. The rule that makes the block sound (this index is in bounds, this pointer is non-null and aligned, this lifetime outlives the borrow) exists only in your head until you write it down.

A // SAFETY: comment writes it down. It turns an unverifiable "trust me" into a stated claim that a human or a tool can check line by line.

The rule: no comment, no merge

Put a // SAFETY: comment immediately above every unsafe block, naming the invariant the block relies on and why it holds here.

// SAFETY: `idx` is checked against `self.len` on the line above,
// so it is always in bounds for `get_unchecked`.
let elem = unsafe { self.data.get_unchecked(idx) };
SituationWhat the comment must state
Raw pointer derefPointer is non-null, aligned, and points to a live, initialized value
get_unchecked / indexingThe index is provably in bounds at this point
from_utf8_uncheckedThe bytes were already validated as UTF-8
FFI callThe foreign contract (ownership, nullability, thread rules) is met
unsafe impl Send/SyncWhy concurrent access is actually sound for this type

The mirror image also matters: a # Safety section in the doc comment of every public unsafe fn tells callers what they must guarantee before calling it. The SAFETY: comment discharges that obligation at the call site.

Enforce it in CI, not in code review

You don't have to police this by hand. Clippy ships two lints for it:

Turn the first into a hard gate:

#![warn(clippy::undocumented_unsafe_blocks)]
// or, to block the merge outright:
#![deny(clippy::undocumented_unsafe_blocks)]

Pair it with #![deny(unsafe_op_in_unsafe_fn)] — the standard-library policy — so that an unsafe fn body doesn't get a free pass: every unsafe operation still needs its own unsafe block, and therefore its own comment. This is exactly how the Rust standard library enforces its safety-comment policy across tens of thousands of unsafe operations.

Why this pays off with AI in the loop

Reviewers stop guessing whether a block is sound and start checking it against a written rule. So do AI agents: given the stated invariant, an agent can verify a change preserves it, or refuse the edit and flag the risk — instead of silently deleting the bounds check that the comment was quietly protecting. The comment is the machine-readable contract that keeps both humans and models honest.

Resources

Shipping Rust that AI agents touch? Yeda AI audits and hardens codebases so both people and models can reason about them.

Talk to us · Read the blog