Skip to content

Rust Safety Coding Guidelines (Advanced / Subtle Pitfalls) #254

@rcseacord

Description

@rcseacord

This file contains more suggestions for guidelines. Please indicate which ones you would like to see turned into rules and which ones not so much. Please provide your rationale and identify any concerns you have (even for rules you would like to see adopted).

  1. Do not depend on function pointer identity across crates

Guideline: Avoid comparing function pointers (fn() values) for equality when the functions originate from different crates or may be inlined across crate boundaries.

Rationale: Cross-crate inlining, LTO, and codegen-unit splitting may cause logically identical functions to have distinct compiled addresses, breaking equality checks.

  1. Treat vtable pointer identity as unstable

Guideline: Do not rely on the address or identity of a trait object’s vtable for correctness, including through pointer comparison or hashing.

Rationale: Vtable instantiation is a compiler implementation detail. Different crates, codegen-units, or optimization levels may generate distinct vtables with identical semantics.

  1. Avoid using pointer equality as a logical predicate

Guideline: Do not rely on raw pointer equality (ptr1 == ptr2) to determine logical equality or uniqueness of data unless the pointers originate from the same allocation and the relationship is explicitly guaranteed.

Rationale: Distinct allocations may contain identical data. Cross-crate LTO and inlining can also move or duplicate code or data, making address comparisons brittle.

  1. Never assume stable symbol addresses across crates

Guideline: Treat symbol addresses (including &STATIC, fn pointers, and vtable addresses) as non-stable across crates and builds. Avoid designs that depend on global identity properties of these symbols.

Rationale: Different compiler settings, incremental build states, or LTO passes may change symbol instantiation and placement.

  1. Do not assume stable struct layout across crates without #[repr(...)]

Guideline: Any type expected to be shared or transmuted across crate boundaries must have an explicit representation attribute (#[repr(C)] or equivalent).

Rationale: Rust’s default layout is unspecified and may differ between crates or compiler versions. This affects FFI, transmutation, DMA/packed buffers, and memory-mapped I/O.

  1. Avoid using mem::transmute or pointer casts across crates unless representation is fully specified

Guideline: Conversions that reinterpret memory (via mem::transmute, pointer::cast, or reading raw bytes into structs) must only target types whose size, alignment, and layout invariants are explicitly defined and documented.

Rationale: Layout is not stable across crates or optimizer settings unless representation is fixed.

  1. Do not depend on compiler optimizations when encoding program invariants

Guideline: Program correctness must not rely on specific optimizer behavior (e.g. inlining, dead-code elimination, codegen-unit merging). Invariants must hold independent of optimization level and backend.

Rationale: Optimizers may generate different control flow, merge or duplicate functions, or eliminate expected side effects.

  1. Keep identity-sensitive logic within a single crate

Guideline: If a design requires function identity, static identity, or address-based deduplication, the logic must be confined to a single crate or protected with #[inline(never)] and explicit documentation.

Rationale: Within a crate (and with inlining prevented), identity semantics are much more stable and predictable.

  1. Avoid cross-crate unsafe abstractions that require stable metadata

Guideline: Unsafe abstractions must not rely on metadata that can vary between crates (vtable pointers, symbol addresses, layout padding, etc.). Unsafe APIs should enforce all invariants at boundaries.

Rationale: Safe code in a dependent crate may unknowingly break the assumptions of unsafe code unless invariants are fully encapsulated.

  1. Validate behavior under multiple build profiles

Guideline: For any code relying on layout guarantees, identity semantics, pointer stability, or trait object behavior, test across:

debug vs release,

LTO vs non-LTO,

different codegen-unit counts,

incremental vs non-incremental builds.

Rationale: Subtle identity- or layout-related bugs may appear only under certain compilation profiles (as seen in #117047). Safety-critical systems should proactively detect these divergences.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions