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;
}重要细节:
- 整数除法截断:不是四舍五入,也不是向下取整,而是朝零截断(7/2=3,-7/2=-3)。
- 没有 ++ 和 --:这是 Rust 故意的——这两个运算符在 C/C++ 里引发过无数 bug(尤其前缀后缀混用)。Rust 强制你写
+= 1,更明确。 - 类型必须一致:i32 不能直接和 f64 相加,先 as 转换。
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
}记住:
- .. 是半开区间(不含右端),更常见,适合做"前 N 个"切片。
- ..= 是闭区间(含右端),适合明确"到第 N 个"。
- 切片
&arr[1..4]取索引 1、2、3 的元素(不含 4),这是 Rust 切片语法,极常用。 - 反向范围是空的,要用
rev()反转。
7. 类型转换运算符 as
上一篇数据类型已经讲过 as。它是一个运算符,把一种数字类型转成另一种:x as f64、n 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 控制流 →