将`bool`转换为`u8`

7
以下代码在amd64架构上,使用Rust 1.8版本可以正常运行。
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);
}

我的问题是,我可以假设这总是有效的吗?我尝试查找有关bool表示的参考资料,但我只找到了这个这个,但我认为那不是权威的。

6
为什么会这样做?(插入夸张的喘息声。) - Veedrac
2
创建一个紧凑的布尔选项类型,类似于 https://github.com/llogiq/optional。可选箱使用枚举,但这不允许返回对内部值的引用。 - malbarbo
没有看到任何功能上的区别,与 let f: u8 = if boolValue { 1u8 } else { 0u8 } 这样的代码相比(不确定 Rust 语法的确切形式)。但是没有使用 transmute 的版本将在不依赖于任何编译器行为的情况下工作。 - Matthias247
我需要转换功能,以满足我在另一条评论中描述的目的。 - malbarbo
3个回答

6
2021年更新:Rust参考手册现已将bool的内存表示定义为一个1字节的值,等于0或1:

布尔类型的对象每个都有1个字节的大小和对齐方式。值false的位模式为0x00,true的位模式为0x01。 对于具有布尔类型的对象使用任何其他位模式均属于未定义行为。


最后,这本书并不是规范性的。它可能包含一些特定于rustc本身的细节,并且不应被视为Rust语言的规范。特别地,https://github.com/rust-lang/reference/pull/940是由一个合作者编写的,而不是语言核心团队,因此我不会将其用作参考。 - Stargateur
2
@Stargateur Rust 据我所知没有规范的规范。如果参考书的这一页不打算定义 bool 的内存表示,我认为我们应该提交一个文档错误,因为这一页相当权威地说明了一种表示。 - Tim McLean
@Stargateur 根据该拉取请求,bool 的允许值已在 src/behavior-considered-undefined.md 中指定...(请参见“更改的文件”选项卡中的第一个文件) - Tim McLean

4

布尔表示似乎非常严格。它表示为1和0,但我想警告您,如果由于某种疯狂的原因而更改了这一点,则如果您盲目地假设true == <some u8 that isn't what Rust really uses>,则会出现一些奇怪的行为。这与您的问题相反,但我认为它很重要:

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);
}

这在我测试的Playground配置中几乎每个都会产生不同的输出。同一程序中经常存在完全相反的矛盾:

调试/稳定版:

true true true
evaluates false
0
0

这意味着它打印为真,在与真和假比较时都视为真,但在分支中评估为假。并转换回0。

发布/稳定版:

true false true
evaluates true
4
4

如果你使用的是C-style bool,那么这可能是你所期望的,并具有正确的转换行为。(编辑:实际上不是。它打印错误!它与其评估方式相反比较)。

调试/测试版:

true true true
evaluates false
4
4

与 Debug/Stable 相同,但会正确地转换回来(我认为这可能是一个被修复的 bug)。

发布版 / Beta 版:

与 Release/Stable 相同。

调试版 / 夜间版:

与 Debug/Beta 相同。

发布版 / 夜间版:

对于其他版本,与 Release 相同。

额外内容

如果您将 println!("{} {} {}", b, b == true, b == false); 更改为 println!("{} {}", b, b == true);,则会获得不同的输出行为。
例如,在 Debug/Stable 上:
true false
evaluates false
0
0

此外,1 as u8 转换在所有配置上都能正常工作,因此这不仅仅是一个 transmute 的问题。
总之,尽管这很不可能改变,但如果真的发生了改变(或者您在使用 u8 出现错误并通过 transmute 或使用不安全指针进行更改),那么您可能会遇到一种非常棘手的 Heisenbug。对于大多数情况,我建议继续使用完全有效且安全的 my_bool as u8。尽管我知道您的用例可能禁止这样做。

感谢您的回答。正如您所说,这与我的问题方向相反。不管怎样,这是一个有趣的答案。 - malbarbo

1

从未有任何RFC采用定义bool的表示方式。 话虽如此,实际上它很少会改变。


相关链接:https://github.com/rust-lang/rust/pull/46156 - aksh1618

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接