7

The following code works correctly with Rust 1.8 on amd64.

use std::mem;

fn main() {
    let f: u8 = unsafe { mem::transmute(false) };
    let t: u8 = unsafe { mem::transmute(true) };
    assert_eq!(0, f);
    assert_eq!(1, t);
}

My question is, can I assume that this will always works? I tried to find a reference about bool representation, but I only found this and this, but I think that is not authoritative.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
malbarbo
  • 10,717
  • 1
  • 42
  • 57
  • 6
    Why would you do this? (Insert melodramatic gasps.) – Veedrac Apr 28 '16 at 20:08
  • 2
    To create a compact option type for bool, similar to https://github.com/llogiq/optional. The optional crate uses an enumeration, but that do not allow to return a reference to the inner value. – malbarbo Apr 28 '16 at 20:29
  • Don't see any functional difference against something like `let f: u8 = if boolValue { 1u8 } else { 0u8 }` (not sure about the exact rust syntax). But the version without transmute will work without relying on any compiler behavior. – Matthias247 Apr 28 '16 at 20:51
  • I need the transmute for the purpose I described in the other comment. – malbarbo Apr 28 '16 at 20:58

3 Answers3

6

Update as of 2021: The Rust reference now defines the memory representation for bool as a 1-byte value equal to either 0 or 1:

An object with the boolean type has a size and alignment of 1 each. The value false has the bit pattern 0x00 and the value true has the bit pattern 0x01. It is undefined behavior for an object with the boolean type to have any other bit pattern.

Tim McLean
  • 1,576
  • 3
  • 14
  • 20
  • > Finally, this book is not normative. It may include details that are specific to rustc itself, and should not be taken as a specification for the Rust language. Specially, https://github.com/rust-lang/reference/pull/940, have been write by a collaborator not a language core team so I wouldn't use it as reference. – Stargateur May 23 '21 at 03:14
  • 1
    @Stargateur Rust has no normative spec afaik. If this page of the reference book is not intended to define the memory representation of bool, I think we should file a documentation bug as this page states a representation pretty authoritatively. – Tim McLean May 24 '21 at 05:30
  • @Stargateur Also, according to that pull request, the allowed values for bool were already specified in `src/behavior-considered-undefined.md`... (see first file in Files Changed tab) – Tim McLean May 24 '21 at 05:32
3

The bool representation seems to be very strict. It is represented as 1 and 0, but I'd like to caution that if for some insane reason this changes you'll get some strange behavior if you blindly assume that true == <some u8 that isn't what Rust really uses>. This is the opposite direction to your question, but I think it makes a point:

fn main() {
    use std::mem;

    let b: bool = unsafe {mem::transmute(4 as u8)};

    println!("{} {} {}", b, b == true, b == false);

    if b {
        println!("evaluates true");
    }

    if !b {
        println!("evaluates false");
    }

    let x: u8 = unsafe{mem::transmute(b)};

    println!("{}", x);

    let x = b as u8;

    println!("{}", x);
}

This produces a different output on nearly every configuration on that Playground I tested it on. With frequent flat out contradictions within the same program:

Debug/Stable:

true true true
evaluates false
0
0

This means it prints as true, compares as true with both true and false, but evaluates in a branch as false. And transmutes back to 0.

Release/Stable:

true false true
evaluates true
4
4

This is probably what you'd "expect" if you were using a C-style bool, and has the correct transmute behavior. (Edit: actually, no it's not. It prints wrong! It compares the opposite to how it evaluates).

Debug/Beta:

true true true
evaluates false
4
4

Same as Debug/Stable, but transmutes back correctly (I assume this was probably a bug that got fixed).

Release/Beta:

Same as Release/Stable

Debug/Nightly:

Same as Debug/Beta

Release/Nightly:

Same as Release for others.

Bonus

If you change println!("{} {} {}", b, b == true, b == false); to println!("{} {}", b, b == true); you get different printing behavior.

For instance, on Debug/Stable:

true false
evaluates false
0
0

In addition, transmuting from 1 as u8 works as expected on all configurations, so it's not solely a transmute issue.


The moral of the story is that, while this is unlikely to change, you potentially have one hell of a Heisenbug on your hands if it does (or you make a mistake with the u8 and transmute it back or change it with an unsafe pointer). For most cases, I'd probably just stick with the perfectly working and safe my_bool as u8, though I understand your use case may prohibit this.

Linear
  • 21,074
  • 4
  • 59
  • 70
  • Thanks for your answer. As you sad, this is the opposite direction of my question. Anyway, it was a interesting answers. – malbarbo May 12 '16 at 16:16
1

There was never any RFC adopted defining the representation of bool. That said, as a practical matter it's very unlikely it will change.

Eli Friedman
  • 2,343
  • 1
  • 13
  • 11