May 03, 2025
2 min read
Rust,

在 rust 中使用 位掩码来做条件判断

在 Rust 中,使用位掩码(Bit Masks)进行条件判断是一种高效处理多状态标志的经典方法,尤其适合需要高性能和低内存占用的场景。

Linux 的(rwx) 权限系统就是基于位掩码来做的。

我们先了解一下基本的位运算:

运算符Rust 语法作用示例
AND&保留双方为1的位0b1010 & 0b1100 = 0b1000
OR|任意一方为1则置10b1010 | 0b1100 = 0b1110
XOR^不同则为10b1010 ^ 0b1100 = 0b0110
NOT!按位取反!0b1010 = 0b0101(取决于类型位数)
左移<<向左移位,低位补00b0001 << 3 = 0b1000
右移>>向右移位,高位补符号位(算术右移)0b1000_0000 >> 3 = 0b1111_0000(i8类型)
一个字节有8bit,每个 bit 位都可以代表一个布尔状态,比如我们假定0表示关, 1 表示开。

我们直接拿权限系统 作为例子,先定义一个结构:

#[derive(Debug,Clone,Copy)]
pub struct Permissions(u8);

impl Permissions { 
    const READ: u8    = 0b0000_0001; // 1 << 0
    const WRITE: u8   = 0b0000_0010; // 1 << 1
    const EXECUTE: u8 = 0b0000_0100; // 1 << 2
    const DELETE: u8  = 0b0000_1000; // 1 << 3
}

使用后四个 bit 位分别对应四种权限: 读、写、执行、删除。

假设,有一个待验证的权限,它的值是3,那么它的二进制表示为 0b0000_0011, 我们就可以知道,这个得验证的权限有读和写2个权限。因为 0b0000_0011 可以通过 0b0000_0001 | 0b0000_0010 得到。

我们可以通过下面的代码查看这行待验证的值是否有某个权限:

    // 假设这是一个等验证的权限
    let mut perms = Permissions(Permissions::READ | Permissions::WRITE);
    // 或者 let mut perms = 3;

	// 验证 perms 是否存在读权限
    println!("{:?}", (perms.0 & Permissions::READ) != 0); //true
    // 验证 perms 是否存在写权限
    println!("{:?}", (perms.0 & Permissions::WRITE) != 0); //true
    // 验证 perms 存在读写权限
    println!("{:?}", (perms.0 & (Permissions::READ | Permissions::WRITE)) 
        == (Permissions::READ | Permissions::WRITE));//true
    // 验证 perms 是否存在删除权限
    println!("{:?}", (perms.0 & Permissions::DELETE) != 0);

因为 0 的二进制表示 0b00000000,这直接表示无任何权限。以判断读权限为例子,通过 & 位运算(双方为1时,结果为1,否则为0):

0b0000_0011 --> 等验证的值,这里以3为例子
0b0000_0001 --> 这是读权限
		  & --> 按位与,保留双方为1的位
===========
0b0000_0001 --> 结果为1,表示有读权限

联合权限判断时,刚好可以按位或 | 的特性,把所有为1的位保留了下来。比如,判断是否同时存在读和写权限时使用的 Permissions::READ | Permissions::WRITE:

0b0000_0001
0b0000_0010
		  |
===========
0b0000_0011 

判断其他权限也同理。

bitflags

有趣的是,有人基于此特点写了个更好语义和用户友好的API使用的库 - bitflags

cargo add bitflags

这个库能实现更复杂,但语义上看上去更简单的写法,比如下面这个官方例子:

bitflags! {
	/// 表示一组标志。
	#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
	struct Flags: u32 {
	    /// 值 `A`,位于第 `0` 位。
	    const A = 0b00000001;

	    /// 值 `B`,位于第 `1` 位。
	    const B = 0b00000010;

	    /// 值 `C`,位于第 `2` 位。
	    const C = 0b00000100;

	    /// `A`、`B` 和 `C` 的组合。
	    const ABC = Self::A.bits() | Self::B.bits() | Self::C.bits();
	}
}

fn main() {
	let e1 = Flags::A | Flags::C;  // 包含 A 和 C 的集合
	let e2 = Flags::B | Flags::C;  // 包含 B 和 C 的集合

	assert_eq!((e1 | e2), Flags::ABC);   // 并集结果是包含 A、B、C 的集合
	assert_eq!((e1 & e2), Flags::C);     // 交集结果只包含 C
	assert_eq!((e1 - e2), Flags::A);     // 差集结果只包含 A
	assert_eq!(!e2, Flags::A); // 补集结果表示不在 e2 中的位(在此例中是 A)
}