Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Niche optimization: why is `size_of::<Result<bool, bool>>()` 2 instead of 1?

Niche optimization allows Rust to store data in the invalid bit patterns of a type. The only valid values of a bool are 0 and 1 (one bit). And Result only has two variants (one bit). So why does Result<bool, bool> take two bytes instead of 1? I would expect the first bit to be the bool value and the second to be the Result variant, as is the case with Option<bool>, which does does indeed have size 1. It seems that giving the second variant a payload, even when it's the same size as the first variant’s payload, affects niche optimization, but I don't see why that would be the case.

like image 404
BallpointBen Avatar asked Jan 22 '26 16:01

BallpointBen


2 Answers

Because the payload of an enum variant has to be individually addressable, at addressing unit (i.e. byte) granularity.

Take this example:

fn change(b: &mut bool); // opaque

fn get_changed(mut r: Result<bool, bool>) -> Result<bool, bool> {
  match r {
    Ok(ref mut b) => change(b),
    Err(ref mut b) => change(b),
  };
  r
}

change must be able to manipulate the borrowed bool without knowing it is actually embedded within a Result<_, _> and has to have some of its bits preserved. For example, change may perform a bytewise memory copy into the place borrowed by b.

For a more drastic example:

fn swap_payloads(r: &mut Result<bool, bool>, s: &mut Result<bool, bool>) {
  let a: &mut bool = match r { Ok(x) => x, Err(x) => x, };
  let b: &mut bool = match s { Ok(x) => x, Err(x) => x, };
  std::mem::swap(a, b);
}

std::mem::swap performs a bytewise swap. If Result<bool, bool> were packed into a single byte, std::mem::swap would have swapped not just payloads, but discriminants as well.

For this reason, the payload of a Result<bool, bool> must be stored at a distinct address from the discriminant.

There have been discussions about adding a feature that can prohibit referencing sub-fields within a data type, so that this optimization can happen (e.g. https://internals.rust-lang.org/t/towards-even-smaller-structs/14686/), but it seems none have come to fruition as of 2025.

like image 99
user3840170 Avatar answered Jan 25 '26 11:01

user3840170


Because you can take a reference to the bool, and give it to code that knows nothing about the original type. Therefore the value of the whole byte of bool must be 1 or 0, it cannot be anything else.

like image 26
Chayim Friedman Avatar answered Jan 25 '26 12:01

Chayim Friedman



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!