Peculiar behavior when doing bad things

Here's an example of the difference, running the same code with a pointer -- which is allowed to be null -- and a reference -- which is not allowed to be null: https://rust.godbolt.org/z/zxPMT1eeG

pub fn demo1(mut n: u32, x: &u32) -> u32 {
    let mut sum = 0;
    while n > 0 {
        n -= 1;
        sum += *x;
    }
    sum
}

pub unsafe fn demo2(mut n: u32, x: *const u32) -> u32 {
    let mut sum = 0;
    while n > 0 {
        n -= 1;
        sum += *x;
    }
    sum
}

With the reference, that compiles to just n * *x, because references are never null

example::demo1:
        mov     eax, edi
        imul    eax, dword ptr [rsi]
        ret

But because pointers are allowed to be null, that one compiles to if n != 0 { n * *x } else { 0 } because it has to make sure it doesn't read through a null pointer:

example::demo2:
        test    edi, edi
        je      .LBB1_1
        mov     eax, edi
        imul    eax, dword ptr [rsi]
        ret
.LBB1_1:
        xor     eax, eax
        ret

That's why no, you're not allowed to make references be null even in unsafe code. If you want something to be nullable, use pointers or options-of-references.

Also, if you're wondering why the goldbolt link uses an old version of rustc, it's because there's a been a regression in LLVM. Looks like it's fixed in LLVM trunk, though, as I can repro the same thing in clang 13: https://cpp.godbolt.org/z/8bKb5WzTv

7 Likes