Rust专项——掌握运算与表达式
·
引言
运算符和表达式是构成程序逻辑的基础。Rust提供了丰富的运算符,涵盖了算术运算、逻辑判断、位操作等各个方面。同时,Rust对表达式和语句的严格区分也是其独特的设计特色,理解这一点对掌握Rust编程至关重要。
本文将全面介绍Rust的运算符和表达式系统,包括:
- 算术运算符与赋值运算符
- 比较运算符与逻辑运算符
- 位运算符与复合赋值运算符
- 运算符优先级与结合性
- 表达式与语句的区别
- 运算符重载基础
- 最佳实践和常见陷阱
通过本文的学习,你将能够:
- 熟练使用各种运算符进行运算
- 理解运算符的优先级和结合性
- 掌握表达式与语句的差异
- 写出更清晰、更高效的Rust代码
1. 表达式与语句(Expressions vs Statements)
1.1 什么是表达式?
表达式(Expression) 是计算并产生值的代码片段:
fn main() {
// 这些都是表达式,都会产生值
let x = 5; // 5 是表达式
let y = x + 3; // x + 3 是表达式,产生值 8
let z = {
let a = 10;
let b = 20;
a + b // 注意:没有分号,这是一个表达式
}; // z = 30
println!("x = {}, y = {}, z = {}", x, y, z);
}

1.2 什么是语句?
语句(Statement) 是执行某些操作但不返回值的代码片段:
fn main() {
// let 语句:创建变量,但不返回值
let x = 5; // 语句
// 函数调用也可以是语句
println!("Hello"); // 语句
// 分号将表达式转换为语句
let y = {
10 // 表达式
}; // 语句
}
1.3 表达式与语句的区别
| 特性 | 表达式 | 语句 |
|---|---|---|
| 产生值 | ✅ 是 | ❌ 否 |
| 分号 | 可无 | 必须有 |
| 可赋值 | ✅ 是 | ❌ 否 |
| 示例 | 5, x + 1, { a + b } |
let x = 5;, x = 10; |
关键示例:
fn main() {
// 正确:表达式块
let x = {
let a = 5;
let b = 10;
a + b // 没有分号,这是表达式,返回值
}; // x = 15
// 错误:如果加上分号,就变成了语句
let y = {
let a = 5;
let b = 10;
a + b; // 有分号,变成语句,返回单元类型 ()
}; // y = (),类型不匹配!
println!("x = {}", x);
}

2. 算术运算符(Arithmetic Operators)
2.1 基本算术运算符
fn main() {
let a = 10;
let b = 3;
// 加法
let sum = a + b;
println!("{} + {} = {}", a, b, sum); // 13
// 减法
let diff = a - b;
println!("{} - {} = {}", a, b, diff); // 7
// 乘法
let prod = a * b;
println!("{} * {} = {}", a, b, prod); // 30
// 除法(整数除法)
let quot = a / b;
println!("{} / {} = {}", a, b, quot); // 3(整数除法)
// 取余
let rem = a % b;
println!("{} % {} = {}", a, b, rem); // 1
// 浮点数运算
let x: f64 = 10.0;
let y: f64 = 3.0;
println!("{} / {} = {:.2}", x, y, x / y); // 3.33
}
2.2 算术运算符与类型
fn main() {
let a: i32 = 10;
let b: i32 = 3;
// 同类型运算
let result = a + b; // i32
// 不同类型需要转换
let c: i64 = 100;
// let sum = a + c; // 错误!类型不匹配
let sum = a as i64 + c; // 需要显式转换
println!("和: {}", sum);
// 注意整数除法的截断
let x = 10 / 3;
println!("10 / 3 = {} (整数除法)", x); // 3,不是 3.33
// 浮点数除法
let y = 10.0 / 3.0;
println!("10.0 / 3.0 = {:.2}", y); // 3.33
}
2.3 溢出检查
fn main() {
let a: u8 = 255;
// 在调试模式下,这会panic
// let b = a + 1; // panic!
// 使用 checked_* 方法
match a.checked_add(1) {
Some(result) => println!("结果: {}", result),
None => println!("溢出!"),
}
// 使用 wrapping_* 方法
let b = a.wrapping_add(1);
println!("255 + 1 (回绕) = {}", b); // 0
// 使用 saturating_* 方法
let c = a.saturating_add(1);
println!("255 + 1 (饱和) = {}", c); // 255
}
3. 赋值运算符(Assignment Operators)
3.1 基本赋值
fn main() {
// 基本赋值
let mut x = 5;
x = 10;
println!("x = {}", x);
// 赋值表达式返回值(但不常用)
let mut y = 5;
let z = y = 10; // z 是单元类型 ()
println!("y = {}, z = {:?}", y, z);
}
3.2 复合赋值运算符
Rust支持复合赋值运算符:
fn main() {
let mut x = 10;
// 加法赋值
x += 5; // x = x + 5
println!("x += 5: {}", x); // 15
// 减法赋值
x -= 3; // x = x - 3
println!("x -= 3: {}", x); // 12
// 乘法赋值
x *= 2; // x = x * 2
println!("x *= 2: {}", x); // 24
// 除法赋值
x /= 4; // x = x / 4
println!("x /= 4: {}", x); // 6
// 取余赋值
x %= 4; // x = x % 4
println!("x %= 4: {}", x); // 2
// 位运算复合赋值
let mut y = 0b1010u8; // 二进制 1010 = 10
y &= 0b1100; // y = y & 0b1100 = 0b1000 = 8
y |= 0b0011; // y = y | 0b0011 = 0b1011 = 11
y ^= 0b1111; // y = y ^ 0b1111 = 0b0100 = 4
y <<= 1; // y = y << 1 = 8
y >>= 2; // y = y >> 2 = 2
println!("y = {}", y); // 2
}
4. 比较运算符(Comparison Operators)
4.1 相等性与比较
fn main() {
let a = 10;
let b = 20;
let c = 10;
// 相等性比较
println!("{} == {}: {}", a, b, a == b); // false
println!("{} == {}: {}", a, c, a == c); // true
println!("{} != {}: {}", a, b, a != b); // true
// 大小比较
println!("{} < {}: {}", a, b, a < b); // true
println!("{} > {}: {}", a, b, a > b); // false
println!("{} <= {}: {}", a, c, a <= c); // true
println!("{} >= {}: {}", a, b, a >= b); // false
}
4.2 不同类型间的比较
fn main() {
// 同类型比较
let a: i32 = 10;
let b: i32 = 20;
println!("比较结果: {}", a < b);
// 不同类型需要转换
let c: i64 = 15;
// println!("比较: {}", a < c); // 错误!类型不匹配
println!("比较: {}", a as i64 < c);
// 浮点数比较(注意精度问题)
let x: f64 = 0.1 + 0.2;
let y: f64 = 0.3;
println!("0.1 + 0.2 == 0.3: {}", x == y); // false! 精度问题
// 使用近似比较
println!("近似相等: {}", (x - y).abs() < 1e-10);
}
4.3 PartialEq 和 PartialOrd
fn main() {
// 整数完全可比较
let a = 10;
let b = 20;
println!("{} < {}: {}", a, b, a < b);
// 浮点数部分可比较(NaN不可比较)
let x: f64 = 3.14;
let y: f64 = f64::NAN;
// x 可以与数字比较
println!("{} < 10.0: {}", x, x < 10.0);
// NaN 不能比较
// println!("{} < 10.0: {}", y, y < 10.0); // 编译可能通过,但结果为false
println!("y 是 NaN: {}", y.is_nan());
}
5. 逻辑运算符(Logical Operators)
5.1 逻辑与、或、非
fn main() {
let a = true;
let b = false;
// 逻辑与 &&
println!("{} && {} = {}", a, b, a && b); // false
println!("{} && {} = {}", a, true, a && true); // true
// 逻辑或 ||
println!("{} || {} = {}", a, b, a || b); // true
println!("{} || {} = {}", false, b, false || b); // false
// 逻辑非 !
println!("!{} = {}", a, !a); // false
println!("!{} = {}", b, !b); // true
}
5.2 短路求值(Short-circuit Evaluation)
Rust的逻辑运算符使用短路求值:
fn main() {
// 短路求值示例
fn expensive() -> bool {
println!("执行昂贵的操作");
true
}
// && 短路:如果左边为false,右边不执行
let result1 = false && expensive(); // expensive() 不会执行
// || 短路:如果左边为true,右边不执行
let result2 = true || expensive(); // expensive() 不会执行
println!("result1: {}, result2: {}", result1, result2);
// 实际应用:空值检查
let value = Some(42);
if value.is_some() && value.unwrap() > 10 {
println!("值大于10");
}
}
5.3 使用逻辑运算符的条件判断
fn main() {
let age = 25;
let has_license = true;
// 多个条件判断
if age >= 18 && has_license {
println!("可以开车");
}
if age < 18 || !has_license {
println!("不能开车");
}
// 复杂条件
let score = 85;
if score >= 90 || (score >= 80 && score < 90) {
println!("成绩良好");
}
}
6. 位运算符(Bitwise Operators)
6.1 基本位运算
fn main() {
let a: u8 = 0b1100; // 12
let b: u8 = 0b1010; // 10
// 位与 &
let result_and = a & b;
println!("{:b} & {:b} = {:b} ({})", a, b, result_and, result_and); // 0b1000 = 8
// 位或 |
let result_or = a | b;
println!("{:b} | {:b} = {:b} ({})", a, b, result_or, result_or); // 0b1110 = 14
// 位异或 ^
let result_xor = a ^ b;
println!("{:b} ^ {:b} = {:b} ({})", a, b, result_xor, result_xor); // 0b0110 = 6
// 位非 ~
let result_not = !a;
println!("!{:b} = {:b} ({})", a, result_not, result_not);
// 左移 <<
let result_left = a << 2;
println!("{} << 2 = {} ({:b})", a, result_left, result_left); // 48
// 右移 >>
let result_right = a >> 2;
println!("{} >> 2 = {} ({:b})", a, result_right, result_right); // 3
}
6.2 位运算的实际应用
fn main() {
// 应用1:检查位的设置
fn is_bit_set(num: u8, bit: u8) -> bool {
(num & (1 << bit)) != 0
}
let num = 0b1010u8; // 10
println!("检查第1位: {}", is_bit_set(num, 1)); // true
println!("检查第0位: {}", is_bit_set(num, 0)); // false
// 应用2:设置位
fn set_bit(num: u8, bit: u8) -> u8 {
num | (1 << bit)
}
let num2 = set_bit(0b1000, 1);
println!("设置位后: {:b}", num2); // 0b1010
// 应用3:清除位
fn clear_bit(num: u8, bit: u8) -> u8 {
num & !(1 << bit)
}
let num3 = clear_bit(0b1111, 2);
println!("清除位后: {:b}", num3); // 0b1011
// 应用4:快速乘除(位移)
let x = 10;
println!("{} * 2 = {}", x, x << 1); // 20(左移1位等于乘2)
println!("{} / 2 = {}", x, x >> 1); // 5(右移1位等于除2)
}
7. 运算符优先级(Operator Precedence)
7.1 优先级规则
Rust运算符的优先级从高到低:
| 优先级 | 运算符 | 说明 |
|---|---|---|
| 最高 | () [] . |
括号、索引、成员访问 |
! ~ - * & &mut |
一元运算符 | |
* / % |
乘除模 | |
+ - |
加减 | |
<< >> |
位移 | |
& |
位与 | |
^ |
位异或 | |
| |
位或 | |
== != < > <= >= |
比较 | |
&& |
逻辑与 | |
|| |
逻辑或 | |
.. ..= |
范围 | |
= += -= *= /= %= |
赋值 | |
| 最低 | , |
逗号 |
7.2 优先级示例
fn main() {
// 优先级示例
let result1 = 2 + 3 * 4; // 先算乘法,2 + 12 = 14
println!("2 + 3 * 4 = {}", result1);
let result2 = (2 + 3) * 4; // 括号优先,5 * 4 = 20
println!("(2 + 3) * 4 = {}", result2);
// 逻辑运算符优先级
let a = true;
let b = false;
let c = true;
// && 优先级高于 ||
let result3 = a || b && c; // true || (false && true) = true || false = true
println!("{} || {} && {} = {}", a, b, c, result3);
// 使用括号明确意图
let result4 = (a || b) && c; // (true || false) && true = true && true = true
println!("({} || {}) && {} = {}", a, b, c, result4);
// 复合运算符
let mut x = 5;
x = x * 2 + 1; // 先算 5*2+1 = 11,再赋值
println!("x = {}", x);
}
7.3 使用括号提高可读性
即使运算符优先级正确,使用括号也能提高代码可读性:
fn main() {
// 虽然正确,但不够清晰
let result1 = a + b * c - d / e;
// 使用括号更清晰
let result2 = a + (b * c) - (d / e);
// 复杂表达式
let x = 10;
let y = 20;
let z = 5;
// 清晰写法
let result = (x + y) * z / 2;
println!("({} + {}) * {} / 2 = {}", x, y, z, result);
}
8. 范围运算符(Range Operators)
8.1 基本范围运算符
fn main() {
// 范围类型
let range1 = 1..5; // [1, 5),不包含5
let range2 = 1..=5; // [1, 5],包含5
let range3 = ..5; // 从开始到5(不包含)
let range4 = 1..; // 从1到结束
// 在for循环中使用
println!("使用 .. 范围:");
for i in 1..5 {
print!("{} ", i); // 1 2 3 4
}
println!();
println!("使用 ..= 范围:");
for i in 1..=5 {
print!("{} ", i); // 1 2 3 4 5
}
println!();
}
8.2 范围的索引用途
fn main() {
let arr = [10, 20, 30, 40, 50];
// 使用范围索引数组
let slice1 = &arr[1..4]; // [20, 30, 40]
println!("切片1: {:?}", slice1);
let slice2 = &arr[..3]; // [10, 20, 30]
println!("切片2: {:?}", slice2);
let slice3 = &arr[2..]; // [30, 40, 50]
println!("切片3: {:?}", slice3);
// 字符串切片
let s = "Hello, Rust!";
let substring = &s[0..5];
println!("子字符串: {}", substring); // "Hello"
}
9. 方法调用运算符(Method Call Operator)
9.1 点运算符
fn main() {
let s = String::from("Hello, Rust!");
// 方法调用
let len = s.len();
println!("字符串长度: {}", len);
// 链式调用
let upper = s.to_uppercase()
.replace("RUST", "WORLD")
.trim()
.to_string();
println!("处理后: {}", upper);
// 借用方法的调用
let num = 42;
let num_str = num.to_string(); // to_string 方法
println!("数字转字符串: {}", num_str);
}
9.2 关联函数调用
fn main() {
// 使用 :: 调用关联函数
let s1 = String::from("Hello");
let s2 = String::new();
let s3 = String::with_capacity(10);
// 类型转换
let num: i32 = "42".parse().unwrap();
// 集合创建
let vec = Vec::new();
let arr = [1, 2, 3];
println!("各种类型创建示例");
}
10. 类型转换运算符(Type Cast Operator)
10.1 as 运算符
fn main() {
// 整数类型转换
let x: i32 = 42;
let y: i64 = x as i64;
let z: u32 = x as u32;
println!("{} (i32) -> {} (i64) -> {} (u32)", x, y, z);
// 浮点数转换
let f: f64 = 3.14159;
let i = f as i32; // 截断,不是四舍五入
println!("{} -> {}", f, i);
// 字符转换
let c = 'A';
let ascii = c as u8;
println!("'{}' -> {}", c, ascii);
// 指针转换(高级用法)
let ptr = &x as *const i32 as usize;
println!("指针值: {}", ptr);
}
10.2 安全的类型转换
fn main() {
// 使用 parse 方法(返回 Result)
match "42".parse::<i32>() {
Ok(num) => println!("转换成功: {}", num),
Err(e) => println!("转换失败: {}", e),
}
// 使用 TryFrom trait
use std::convert::TryFrom;
match u8::try_from(300u16) {
Ok(val) => println!("转换成功: {}", val),
Err(_) => println!("转换失败:超出范围"),
}
}
11. 问号运算符(? Operator)
11.1 错误传播
? 运算符用于简化错误处理:
use std::fs::File;
use std::io::Read;
fn read_file_contents(filename: &str) -> Result<String, std::io::Error> {
let mut file = File::open(filename)?; // 如果失败,返回错误
let mut contents = String::new();
file.read_to_string(&mut contents)?; // 如果失败,返回错误
Ok(contents)
}
fn main() {
match read_file_contents("example.txt") {
Ok(contents) => println!("文件内容: {}", contents),
Err(e) => println!("读取失败: {}", e),
}
}
12. 实战示例
示例1:简单计算器
fn main() {
let a = 10.0;
let b = 3.0;
println!("=== 计算器示例 ===");
println!("a = {}, b = {}", a, b);
println!("加法: {} + {} = {}", a, b, a + b);
println!("减法: {} - {} = {}", a, b, a - b);
println!("乘法: {} * {} = {}", a, b, a * b);
println!("除法: {} / {} = {:.2}", a, b, a / b);
println!("取余: {} % {} = {}", a, b, a % b);
// 复杂表达式
let result = (a + b) * (a - b) / 2.0;
println!("复杂计算: ({} + {}) * ({} - {}) / 2 = {:.2}",
a, b, a, b, result);
}
示例2:位运算工具
fn main() {
println!("=== 位运算工具 ===");
// 设置标志位
const READ: u8 = 1 << 0; // 0b0001
const WRITE: u8 = 1 << 1; // 0b0010
const EXEC: u8 = 1 << 2; // 0b0100
let mut permissions = 0u8;
// 添加权限
permissions |= READ | WRITE;
println!("权限: {:b}", permissions); // 0b0011
// 检查权限
if permissions & READ != 0 {
println!("有读权限");
}
if permissions & EXEC == 0 {
println!("没有执行权限");
}
// 移除权限
permissions &= !WRITE;
println!("移除写权限后: {:b}", permissions); // 0b0001
}
示例3:条件判断系统
fn main() {
println!("=== 条件判断系统 ===");
let age = 25;
let has_license = true;
let has_car = false;
let score = 85;
// 复合条件判断
let can_drive = age >= 18 && has_license;
println!("可以开车: {}", can_drive);
let can_travel = (age >= 18 && has_license) || has_car;
println!("可以出行: {}", can_travel);
// 成绩等级判断
let grade = if score >= 90 {
"优秀"
} else if score >= 80 && score < 90 {
"良好"
} else if score >= 60 && score < 80 {
"及格"
} else {
"不及格"
};
println!("分数 {} 的等级: {}", score, grade);
// 复杂逻辑表达式
let is_valid = (age >= 18 && age <= 65)
&& (score >= 60 || has_license);
println!("是否有效: {}", is_valid);
}
示例4:数学函数计算器
fn main() {
println!("=== 数学函数计算器 ===");
let x: f64 = 2.0;
let y: f64 = 3.0;
// 使用表达式计算
let power = x.powf(y); // x^y
let sqrt = x.sqrt();
let abs = (-x).abs();
println!("{} 的 {} 次方 = {}", x, y, power);
println!("{} 的平方根 = {:.2}", x, sqrt);
println!("-{} 的绝对值 = {}", x, abs);
// 复杂数学表达式
let result = (x * y).powf(2.0) + (x / y).sqrt() - x.abs();
println!("复杂表达式结果: {:.2}", result);
// 使用范围
let sum: i32 = (1..=10).sum();
println!("1到10的和: {}", sum);
let product: i32 = (1..=5).product();
println!("1到5的积: {}", product);
}
13. 扩展练习
练习1:基本运算
创建一个程序,接收两个数字,执行所有基本算术运算并显示结果。
练习2:逻辑判断器
编写一个程序,使用逻辑运算符判断多个条件的组合结果,并输出真值表。
练习3:位运算工具
实现一个位操作工具,包括设置位、清除位、翻转位、检查位等功能。
练习4:运算符优先级测试
编写代码测试不同运算符的优先级,验证你对优先级规则的理解。
练习5:表达式计算器
创建一个简单的表达式计算器,能够处理包含多种运算符的表达式。
练习6:类型转换工具
编写一个程序,演示各种类型之间的安全转换和不安全转换。
14. 常见错误与解决方案
错误1:整数溢出
fn main() {
// 问题代码
// let x: u8 = 255;
// let y = x + 1; // 在调试模式下会panic
// 解决方案
let x: u8 = 255;
match x.checked_add(1) {
Some(val) => println!("结果: {}", val),
None => println!("溢出!"),
}
}
错误2:类型不匹配
fn main() {
let a: i32 = 10;
let b: f64 = 3.14;
// 问题代码
// let result = a + b; // 编译错误
// 解决方案
let result = a as f64 + b;
println!("结果: {}", result);
}
错误3:浮点数比较错误
fn main() {
let x: f64 = 0.1 + 0.2;
let y: f64 = 0.3;
// 问题代码
// if x == y { // 可能为false(精度问题)
// println!("相等");
// }
// 解决方案:使用近似比较
if (x - y).abs() < 1e-10 {
println!("近似相等");
}
}
错误4:运算符优先级误解
fn main() {
// 问题代码:可能误解优先级
// let result = 2 + 3 * 4 == 2 + (3 * 4) // 14 != 14?
// 解决方案:使用括号明确意图
let result1 = 2 + 3 * 4; // 14
let result2 = (2 + 3) * 4; // 20
println!("2 + 3 * 4 = {}", result1);
println!("(2 + 3) * 4 = {}", result2);
}
15. 最佳实践
15.1 运算符使用建议
| 场景 | 推荐做法 | 原因 |
|---|---|---|
| 整数除法 | 明确使用整数类型 | 避免意外的浮点数除法 |
| 溢出风险 | 使用 checked_* 方法 |
安全处理溢出 |
| 浮点数比较 | 使用近似比较 | 避免精度问题 |
| 复杂表达式 | 使用括号 | 提高可读性 |
| 位运算 | 使用常量定义 | 避免魔法数字 |
15.2 表达式 vs 语句
- ✅ 优先使用表达式:让代码更简洁
- ✅ 明确区分表达式和语句:理解分号的作用
- ✅ 利用表达式返回值:减少临时变量
- ✅ 注意表达式块:末尾不要加分号(如果需要返回值)
15.3 代码可读性
// ❌ 不推荐:难以理解
let result = a+b*c-d/e;
// ✅ 推荐:清晰易读
let result = a + (b * c) - (d / e);
// ✅ 或者拆分成多行
let product = b * c;
let quotient = d / e;
let result = a + product - quotient;
16. 总结
核心要点回顾
- 表达式与语句:表达式产生值,语句执行操作
- 算术运算符:+、-、*、/、%,注意整数除法
- 比较运算符:==、!=、<、>、<=、>=
- 逻辑运算符:&&、||、!,使用短路求值
- 位运算符:&、|、^、!、<<、>>,用于位操作
- 赋值运算符:=、+=、-=等,支持复合赋值
- 运算符优先级:理解优先级规则,必要时使用括号
关键特性
- ✅ 类型安全:所有运算符都进行类型检查
- ✅ 短路求值:逻辑运算符优化性能
- ✅ 溢出检查:可选择如何处理溢出
- ✅ 表达式优先:Rust是表达式导向的语言
下一步学习
掌握了运算符和表达式后,下一步我们将学习:
- 控制流:if表达式、循环、match表达式
- 函数:函数定义、参数、返回值
- 所有权系统:理解Rust的内存管理核心
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)