{
  "$type": "site.standard.document",
  "bskyPostRef": {
    "cid": "bafyreibxcvtvw2wb5gma45sfrerm2cpwrdiuhwik2bvxbzd336htlsniju",
    "uri": "at://did:plc:ivbknywyskln22er3nkssdhl/app.bsky.feed.post/3mfeafxb6qfc2"
  },
  "path": "/t/idea-pre-rfc-null-free-pointers/23991?page=5#post_84",
  "publishedAt": "2026-02-21T01:11:47.000Z",
  "site": "https://internals.rust-lang.org",
  "tags": [
    "@RalfJung"
  ],
  "textContent": "H4n_uL:\n\n> The revised proposal lists this under migration cost as unmeasured and needing benchmarks.\n\nAs promised in my previous comment, I've measured the codegen impact of `null_pointer_is_valid`. This addresses a gap that @RalfJung rightly pointed out, and that several others in the thread have implicitly assumed without data.\n\nI am not a compiler engineer, so I kept it to five simple C patterns on `aarch64-unknown-none` with Homebrew clang 21.1.8 at `-O2`, comparing baseline against `-fno-delete-null-pointer-checks`.\n\n\n    // bench.c\n\n    // clang -v | Homebrew clang version 21.1.8\n    // clang --target=aarch64-unknown-none -O2 -S bench.c -o baseline.s\n    // clang --target=aarch64-unknown-none -O2 -fno-delete-null-pointer-checks -S bench.c -o zeroisvalid.s\n    // diff baseline.s zeroisvalid.s\n\n    #include <stdint.h>\n    #include <stddef.h>\n\n    // C1: null check elimination after dereference\n    uint32_t check_after_deref(const uint32_t *p) {\n        uint32_t v = *p;     // dereference\n        if (p) return v + 1; // compiler can assume p != null\n        return 0;            // baseline: this branch eliminated\n    }\n\n    // C2: two paths merging on null knowledge\n    uint32_t branch_after_store(uint32_t *p, uint32_t val) {\n        *p = val;             // store implies p != null\n        if (p) return *p + 1; // redundant check?\n        return 42;\n    }\n\n    // C3: loop with pointer increment\n    uint32_t sum_until_null(const uint32_t *const *ptrs) {\n        uint32_t sum = 0;\n        while (*ptrs) { // null-terminated array of pointers\n            sum += **(ptrs++);\n        }\n        return sum;\n    }\n\n    // C4: devirtualisation / inlining based on nonnull\n    void copy_if_valid(uint32_t *dst, const uint32_t *src, size_t n) {\n        if (dst && src) {\n            for (size_t i = 0; i < n; i++)\n                dst[i] = src[i];\n        }\n    }\n\n    // C5: struct access implying nonnull\n    uint32_t read_two_fields(const struct { uint32_t a; uint32_t b; } *s) {\n        uint32_t x = s->a;      // implies s != null\n        if (s) return x + s->b; // check eliminable?\n        return 0;\n    }\n\n\n\n    8a9,10\n    >       cbz     x0, .LBB0_2\n    > // %bb.1:\n    10a13\n    > .LBB0_2:\n    21a25,26\n    >       mov     w9, #42                         // =0x2a\n    >       cmp     x0, #0\n    23c28\n    <       add     w0, w1, #1\n    ---\n    >       csinc   w0, w9, w1, eq\n    111a117,118\n    >       cbz     x0, .LBB4_2\n    > // %bb.1:\n    113a121\n    > .LBB4_2:\n\n\nThe result: codegen differences appear only where the compiler would otherwise eliminate an implicit null check after a dereference or store: 2–3 additional instructions per site. Explicit null comparisons (C3, C4) produce identical output.\n\nThis doesn't prove the cost is zero for Rust, but given that one of the most performance-sensitive C codebases in existence has operated without this optimisation for over fifteen years, I think the default assumption should be that the cost is manageable unless demonstrated otherwise.",
  "title": "Idea / Pre-RFC: Null-free pointers"
}