在 Rust 中,使用位掩码(Bit Masks)进行条件判断是一种高效处理多状态标志的经典方法,尤其适合需要高性能和低内存占用的场景。
Linux 的(rwx) 权限系统就是基于位掩码来做的。
我们先了解一下基本的位运算:
| 运算符 | Rust 语法 | 作用 | 示例 |
|---|---|---|---|
| AND | & | 保留双方为1的位 | 0b1010 & 0b1100 = 0b1000 |
| OR | | | 任意一方为1则置1 | 0b1010 | 0b1100 = 0b1110 |
| XOR | ^ | 不同则为1 | 0b1010 ^ 0b1100 = 0b0110 |
| NOT | ! | 按位取反 | !0b1010 = 0b0101(取决于类型位数) |
| 左移 | << | 向左移位,低位补0 | 0b0001 << 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)
}