Pointer to `usize` Bug

I stumbled upon something a while ago and thought it might be interesting to share. What do you think the output of this code is?

fn () {
    let :  = {
        let  = 0u8;
        & as *const _ as 
    };

    let :  = {
        let  = 0u8;
        & as *const _ as 
    };

    if    {
        !("{a:?} != {b:?}");
        !("{a:?} != {b:?}");
    }

    if    {
        !("{a:?} == {b:?}");
    }
}

Drumroll please...

140736004887288 != 140736004887288
140736004887288 == 140736004887288

Surprise! Weird things happen in Rust when dealing with pointer-casted integers.

This is actually a bug in LLVM, with details that go over my head. From the looks of things, the issue isn't even going away anytime soon. How fun!

Sources

codeintel::block_f9dfbf3e26ea6374
fn main()
let a: usize
usize

The pointer-sized unsigned integer type.

The size of this primitive is how many bytes it takes to reference any location in memory. For example, on a 32 bit target, this is 4 bytes and on a 64 bit target, this is 8 bytes.

let v: u8
let b: usize
core::cmp::impls
fn ne(&self, other: &Self) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.

docs.rs
std::macros
macro_rules! println

Prints to the standard output, with a newline.

On all platforms, the newline is the LINE FEED character (\n/U+000A) alone (no additional CARRIAGE RETURN (\r/U+000D)).

This macro uses the same syntax as format, but writes to the standard output instead. See std::fmt for more information.

The println! macro will lock the standard output on each call. If you call println! within a hot loop, this behavior may be the bottleneck of the loop. To avoid this, lock stdout with io::stdout().lock():

use std::io::{stdout, Write};

let mut lock = stdout().lock();
writeln!(lock, "hello world").unwrap();

Use println! only for the primary output of your program. Use eprintln! instead to print error and progress messages.

See the formatting documentation in std::fmt for details of the macro argument syntax.

Panics

Panics if writing to io::stdout fails.

Writing to non-blocking stdout can cause an error, which will lead this macro to panic.

Examples

println!(); // prints just a newline
println!("hello there!");
println!("format {} arguments", "some");
let local_variable = "some";
println!("format {local_variable} arguments");
docs.rs
std::macros
macro_rules! panic

Panics the current thread.

This allows a program to terminate immediately and provide feedback to the caller of the program.

This macro is the perfect way to assert conditions in example code and in tests. panic! is closely tied with the unwrap method of both Option and Result enums. Both implementations call panic! when they are set to None or Err variants.

When using panic!() you can specify a string payload that is built using formatting syntax. That payload is used when injecting the panic into the calling Rust thread, causing the thread to panic entirely.

The behavior of the default std hook, i.e. the code that runs directly after the panic is invoked, is to print the message payload to stderr along with the file/line/column information of the panic!() call. You can override the panic hook using std::panic::set_hook(). Inside the hook a panic can be accessed as a &dyn Any + Send, which contains either a &str or String for regular panic!() invocations. (Whether a particular invocation contains the payload at type &str or String is unspecified and can change.) To panic with a value of another other type, panic_any can be used.

See also the macro compile_error, for raising errors during compilation.

When to use panic! vs Result

The Rust language provides two complementary systems for constructing / representing, reporting, propagating, reacting to, and discarding errors. These responsibilities are collectively known as “error handling.” panic! and Result are similar in that they are each the primary interface of their respective error handling systems; however, the meaning these interfaces attach to their errors and the responsibilities they fulfill within their respective error handling systems differ.

The panic! macro is used to construct errors that represent a bug that has been detected in your program. With panic! you provide a message that describes the bug and the language then constructs an error with that message, reports it, and propagates it for you.

Result on the other hand is used to wrap other types that represent either the successful result of some computation, Ok(T), or error types that represent an anticipated runtime failure mode of that computation, Err(E). Result is used alongside user defined types which represent the various anticipated runtime failure modes that the associated computation could encounter. Result must be propagated manually, often with the help of the ? operator and Try trait, and they must be reported manually, often with the help of the Error trait.

For more detailed information about error handling check out the book or the std::result module docs.

Current implementation

If the main thread panics it will terminate all your threads and end your program with code 101.

Editions

Behavior of the panic macros changed over editions.

2021 and later

In Rust 2021 and later, panic! always requires a format string and the applicable format arguments, and is the same in core and std. Use std::panic::panic_any(x) to panic with an arbitrary payload.

2018 and 2015

In Rust Editions prior to 2021, std::panic!(x) with a single argument directly uses that argument as a payload. This is true even if the argument is a string literal. For example, panic!("problem: {reason}") panics with a payload of literally "problem: {reason}" (a &'static str).

core::panic!(x) with a single argument requires that x be &str, but otherwise behaves like std::panic!. In particular, the string need not be a literal, and is not interpreted as a format string.

Examples

panic!();
panic!("this is a terrible mistake!");
panic!("this is a {} {message}", "fancy", message = "message");
std::panic::panic_any(4); // panic with the value of 4 to be collected elsewhere
core::cmp::impls
fn eq(&self, other: &Self) -> bool

Tests for self and other values to be equal, and is used by ==.