unsafe trait and unsafe impl¶
Level: 301 · deep dive
One line: An unsafe fn puts a proof obligation on every caller; an unsafe trait puts it on every implementor — so the unsafe in unsafe impl Send for MyType {} is you signing for a property that generic code elsewhere will rely on without checking.
Stub — an outline, not a lesson. There is no runnable example behind this page yet, so nothing on it has been through the check that backs every other claim in this library. The bullets below are the questions the finished page has to answer.
What it has to cover¶
- The three declarations worth reading first:
pub unsafe auto trait Send↗,pub unsafe auto trait Syncandpub unsafe trait GlobalAlloc↗. Where does the keyword go — on the trait, on eachimpl, and on none of the places the trait is used as a bound? - The compiler enforces the pairing both ways: implementing an
unsafe traitwithoutunsafe implisE0200, and writingunsafe implfor a safe trait such asCloneisE0199. It enforces nothing about whether the promise is true. - Who carries the proof, compared:
unsafe fn— each caller, at each call;unsafe trait— each implementor, once; anunsafe fndeclared inside a safe trait — callers again. Which one fits a contract like "this type may be zeroed" or "this allocator returns memory valid for the layout"? - Why
Ordis safe even thoughBTreeMapdepends on it: the Nomicon ↗ saysBTreeMap's unsafe code "must be written to be robust againstOrdimplementations which aren't actually total". What decides whether a trait's promise isunsafeto make or merely wrong to break? - Writing one honestly:
unsafe impl Send for Wrapperaround a raw pointer, and the// SAFETY:comment that has to argue it. Clippy's allow-by-defaultnon_send_fields_in_send_tyflags anunsafe impl Sendon a struct holding anRc("some fields inHolderare not safe to be sent to another thread") — what does it not catch? - Documenting one: a public
unsafe traitwithout a# Safetysection tripsclippy::missing_safety_doc, warn by default. What must that section list for implementors rather than callers? #[unsafe(no_mangle)]and the other edition-2024 unsafe attributes: the same idea — a promise the compiler cannot check, written where it is made — applied to an attribute instead of a trait.
The trap it exists for¶
unsafe impl Send for Handle {} added to silence the error at thread::spawn, with no argument written. The compiler accepts it without a warning — unsafe impl Sync on a struct holding a Cell compiles cleanly — and the data race it permits now happens in safe code, in a module that never mentions unsafe.
Where this sits¶
- What
unsafeturns off lists implementing an unsafe trait as one of the five powers;SendandSynccovers what those two traits mean; The global allocator implements one for real. This page covers only where the proof obligation sits and who signs for it.
See also¶
- What
unsafeturns off — the five powers, andunsafe fnagainstunsafe {} SendandSync— the two auto traits almost everyunsafe implis for- The global allocator —
unsafe impl GlobalAlloc, and the contract every collection trusts - Marker traits — traits with no methods, where the promise is the whole trait
- What an invariant is — the property an implementor is vouching for
- The drop check —
unsafe impl Dropwith#[may_dangle], another signed promise
If you are coming from another language¶
- C++. No counterpart in the type system: a concept checks that the expressions compile, not that the type keeps a promise, so "this allocator is thread-safe" lives in documentation. Rust's version is the same documentation plus a keyword at each implementation that
grepcan find. - C. A table of function pointers with a comment — "the
allochook must return memory aligned for any type", "the comparator must be consistent" — is anunsafe traitwithout the keyword. The implementor carries the proof in both languages; only Rust marks the place. - Python.
__hash__and__eq__that disagree corrupt dictionary lookups but can never corrupt memory, because the interpreter checks every access. That is theOrd/BTreeMapcategory exactly: a promise that is wrong to break and safe to make, which is why Rust leavesHashandOrdas safe traits. - Java.
SerializableandCloneableare marker interfaces whose implementors promise something the compiler does not check — but breaking the promise throws or misbehaves rather than causing undefined behaviour, so they correspond to Rust's safe marker traits, not toSend.
Po polsku¶
unsafe fn przerzuca obowiązek dowodu na każdego, kto funkcję wywołuje; unsafe trait — na każdego, kto cechę implementuje. Dlatego unsafe impl Send for MojTyp {} to podpis pod obietnicą, na której kod generyczny w zupełnie innym miejscu polega bez sprawdzania. Kompilator pilnuje tylko formy (E0200, gdy brakuje unsafe impl, E0199, gdy jest zbędne), a nie prawdziwości obietnicy — unsafe impl Sync dla struktury z Cell przejdzie bez słowa ostrzeżenia. Ord pozostaje cechą bezpieczną, bo zepsuta implementacja daje złe wyniki, ale nie niezdefiniowane zachowanie.
Szukaj po polsku: niebezpieczna cecha · obowiązek dowodu · rust unsafe trait vs unsafe fn · rust unsafe impl Send raw pointer · nomicon safe unsafe meaning