Rust 运算符

运算符是语言的基本功,大部分 Rust 运算符和 C/Java/JS 一致,但有几个细节不同:整数除法直接截断(不四舍五入)、没有 ++/--、独特的范围运算符。这一篇快速过一遍,重点讲 Rust 与众不同的地方。

1. 算术运算符

最基础的 + - * / %,五类运算符,和数学一致。但有几个细节新手要注意:

fn main() {
    // 算术运算符:+ - * / %
    let a = 10;
    let b = 3;

    println!("{} + {} = {}", a, b, a + b);    // 13
    println!("{} - {} = {}", a, b, a - b);    // 7
    println!("{} * {} = {}", a, b, a * b);    // 30
    println!("{} / {} = {}", a, b, a / b);    // 3  (整数除法,直接截断)
    println!("{} % {} = {}", a, b, a % b);    // 1  (取余)

    // 整数除法:朝零截断(不是四舍五入)
    println!("{}", 7 / 2);     // 3
    println!("{}", -7 / 2);    // -3 (不是 -4)

    // 浮点除法才正常
    println!("{}", 7.0 / 2.0); // 3.5

    // Rust 没有 ++ 和 -- !
    let mut n = 5;
    // n++;           // 编译错误
    n += 1;           // 必须这样写
    n -= 1;
}

重要细节:

2. 比较运算符

比较运算符 == != < > <= >= 返回 bool 值,可以对数字、字符、字符串使用:

fn main() {
    // 比较运算符:== != < > <= >=
    // 返回 bool 值
    println!("{}", 3 == 3);     // true
    println!("{}", 3 != 4);     // true
    println!("{}", 3 < 4);      // true
    println!("{}", 5 > 5);      // false
    println!("{}", 5 >= 5);     // true
    println!("{}", 6 <= 4);     // false

    // 比较运算对整数、浮点、字符、字符串都有效
    println!("{}", 'a' < 'z');         // true(按 Unicode 比较)
    println!("{}", "abc" < "abd");     // true(字典序)
    println!("{}", 3.14 < 3.15);       // true

    // 不同类型不能直接比较,需要先 as 转换
    // 3 == 3.0  // 编译错误!i32 和 f64 不能直接比

Rust 严格区分类型,3 == 3.0 编译错误,因为 i32 和 f64 是不同类型——这避免了 C/C++ 隐式转换带来的诡异 bug。

3. 逻辑运算符

逻辑运算符 &&||! 用于布尔值。具有短路求值特性:

fn main() {
    let tall = true;
    let strong = false;

    // 逻辑与:&&  两个都真才真(短路求值)
    println!("{}", tall && strong);          // false

    // 逻辑或:||  至少一个真就真(短路求值)
    println!("{}", tall || strong);          // true

    // 逻辑非:!  取反
    println!("{}", !tall);                   // false

    // 短路特性:左边能决定结果时,右边不执行
    // 这样写很安全:
    let x: Option<i32> = None;
    if x.is_some() && x.unwrap() > 0 {       // 不会因 unwrap None 而 panic
        println!("正数");
    }
}

短路特性很重要——左边能决定结果时右边不执行。这样你可以安全地先调用 is_some 判断,再 unwrap 取值,不会因为对 None 解包而 panic。这是写 Rust 代码的常见模式。

注意:Rust 的 !逻辑非(对 bool),也是按位非(对整数),编译器按类型推断选择。这是运算符重载,不冲突。

4. 位运算符

对整数进行二进制位级运算,主要用于底层开发(协议解析、加密、嵌入式、状态码):

fn main() {
    let a: u8 = 0b1100;          // 12
    let b: u8 = 0b1010;          // 10

    // 位运算符:& | ^ ! << >>
    println!("{:b}", a & b);     // 1000  (按位与)
    println!("{:b}", a | b);     // 1110  (按位或)
    println!("{:b}", a ^ b);     // 0110  (按位异或)
    println!("{:b}", !a);        // 11110011 (按位非)
    println!("{:b}", a << 2);    // 110000 (左移 2 位)
    println!("{:b}", a >> 1);    // 110   (右移 1 位)

    // 位运算对底层开发(协议解析、加密、嵌入式)很重要
    // 普通业务代码用得少

普通业务代码用得少。但如果做网络协议、文件格式解析,位运算是必备技能。

5. 复合赋值运算符

运算和赋值合二为一,+=-=*=/=%=,以及位运算版本 &=|=^=<<=>>=:

fn main() {
    let mut x = 10;

    // 复合赋值运算符:+= -= *= /= %=
    x += 5;          // x = x + 5 = 15
    x -= 3;          // x = 12
    x *= 2;          // x = 24
    x /= 4;          // x = 6
    x %= 4;          // x = 2

    // 位运算也有复合赋值:&= |= ^= <<= >>=
    let mut flags: u8 = 0b1010;
    flags |= 0b0001;    // 置位
    flags &= !0b1000;   // 清位
    println!("{:b}", flags);   // 0011
}

6. 范围运算符(Rust 特色)

这是 Rust 比 C/Java 方便的地方。两个范围运算符 ....=,创建一个范围对象,可以用于循环和切片:

fn main() {
    // Rust 独特的范围运算符,用于循环和切片

    // ..= 闭区间(包含右端)
    let r1 = 1..=5;     // 1, 2, 3, 4, 5
    for i in r1 {
        print!("{} ", i);   // 1 2 3 4 5
    }
    println!();

    // ..  半开区间(不含右端,更常见)
    let r2 = 1..5;      // 1, 2, 3, 4
    for i in r2 {
        print!("{} ", i);   // 1 2 3 4
    }
    println!();

    // 数组切片:用范围取一部分
    let arr = [10, 20, 30, 40, 50];
    let slice = &arr[1..4];     // [20, 30, 40]
    println!("{:?}", slice);

    // 反向范围是空的
    for i in 5..1 { print!("{}", i); }  // 什么都不输出
    // 想反向用 rev():
    for i in (1..5).rev() { print!("{} ", i); }  // 4 3 2 1
}

记住:

7. 类型转换运算符 as

上一篇数据类型已经讲过 as。它是一个运算符,把一种数字类型转成另一种:x as f64n as u8。注意 as 只能在数值类型之间转换,字符串和数字之间要用方法(如 parse()to_string())。

8. 解引用运算符 * 和借用运算符 &

这两个下一篇讲所有权和借用时会大量出现,这里先简单提:

它们类似 C 的取地址和解引用,但 Rust 的借用检查器会在编译期保证安全(不会悬垂指针)。

9. 运算符优先级

Rust 的优先级和大多数 C 系语言接近。但你不需要全背下来——不确定就加括号,可读性永远比"显得聪明"重要:

fn main() {
    // 运算符优先级(从高到低,只列常用)
    // 1. 路径 ::   方法调用 .   函数调用 ()
    // 2. 一元 -  !  * (解引用)
    // 3. as  (类型转换)
    // 4. * / %
    // 5. + -
    // 6. << >>
    // 7. &  (按位与)
    // 8. ^  (按位异或)
    // 9. |  (按位或)
    // 10. 比较 == != < > <= >=
    // 11. && (逻辑与)
    // 12. || (逻辑或)
    // 13. .. ..=  (范围)
    // 14. = += -= ...

    // 建议:不确定就加括号,可读性比背优先级重要
    let a = 2 + 3 * 4;       // 14,先乘后加
    let b = (2 + 3) * 4;     // 20,括号优先
    println!("{} {}", a, b);
}

实际工程里,推荐用括号明确表达意图,哪怕优先级本来对的。代码是写给人看的,顺便给机器执行。

小结

这一篇快速过完了 Rust 的全部运算符。最该记住的 Rust 特色:没有 ++/--(写 += 1)、整数除法截断范围运算符 .. 和 ..=(循环和切片利器)、不同类型不能隐式转换(必须 as)。下一篇我们看控制流——if、loop、while、for,学会让程序做判断和重复。

← 上一篇 Rust 数据类型

下一篇 Rust 控制流

✈️💬