当前位置: 首页 > java >正文

【Rust】 3. 语句与表达式笔记

一、核心概念

  • 语句 (Statement):执行操作但不返回值的指令。其类型永远是单元类型 ()。

  • 表达式 (Expression):进行计算并产生一个值的代码片段。它总是有具体的类型。

  • 关系:表达式可以是语句的一部分(例如,在 let x = 6; 中,6 是表达式)。语句块(由 {} 包裹)本身也可以是表达式。

二、语句与表达式的区别

  1. 语句不返回值

fn main() {let a = (let b = 6); // 编译错误!
}
  • 错误原因:let b = 6; 是一个语句,它没有返回值。试图将一个语句(其值为 ())赋值给变量 a 会导致类型不匹配错误。
  1. 表达式返回值

fn main() {let b = {let a = 3; // 语句a + 1      // 表达式(注意:没有分号)};println!("The value of b is: {}", b); // 输出:The value of b is: 4
}
  • 关键点:块({})是一个表达式,它的值是块中最后一个表达式的值。

  • 分号的作用:在代码行末尾加上分号 ; 会将表达式转换为语句,从而使其不再返回值。上例中若在 a + 1 后加上分号,则该块的值会变为 (),导致 let b = ();,从而引发类型推断错误。

  1. 总结
  • 表达式产生一个值,有类型。

  • 语句不产生值,类型为 ()。

  • 表达式 + 分号 = 语句。

  • 语句块 ({…}) 可以包装语句和表达式,并使其整体作为一个表达式使用。

  • Rust 是表达式语言:if、while、for、loop、函数体等都可以返回 values。

三、算术与比较表达式

  1. 算术运算符

+ (加), - (减), * (乘), / (除), % (取余)


fn arithmetic_operation_test1() {let x = 100;let y = 10;println!("x+y={}, x-y={}, x*y={}, x/y={}, x%y={}", x+y, x-y, x*y, x/y, x%y);
}
  1. 比较运算符

== (等于), != (不等于), < (小于), > (大于), <= (小于等于), >= (大于等于)

  • 要求:比较双方类型必须相同且满足 PartialEq 约束。

  • 结果类型:bool。

  • 禁止连续比较:

fn compare_test(a: bool, b: bool, c: bool) -> bool {a == b == c // 编译错误!Rust 禁止这种连续比较语法。}
  • 错误分析:a == b 的结果是 bool 类型,再将这个 bool 值与 c (也是 bool) 进行比较在逻辑上是错误的((true == true) == true 在数学上成立,但 Rust 为避免歧义和常见错误,直接禁止此语法)。

四、赋值表达式

  • 形式:左值 = 右值

  • 类型:赋值表达式本身的类型是单元类型 (),值是空元组 ()。

  • 要求:左右两边类型必须相同。

  • 禁止连续赋值:

fn assignment_test() {let x: i32;let y: i32;let z = 10;x = y = z; // 编译错误!因为 `y = z` 的类型是 `()`,无法赋值给 `x: i32`。}

五、语句块表达式

  • 块 ({}) 的值由其最后一行决定。

  • 最后一行是表达式(无分号)=> 块的值即为该表达式的值。

  • 最后一行是语句(有分号)或没有最后一行 => 块的值是 ()。


fn block_expression_test() {let x: () = { println!("Hello World"); }; // 语句,类型为 ()let y: i32 = { println!("Hello World"); 5 }; // 5 是表达式,块的值是 5println!("x = {:?}", x); // 输出: x = ()println!("y = {}", y);   // 输出: y = 5
}

六、控制流表达式

  1. if-else 表达式
  • 条件:必须是 bool 类型。

  • 括号:不需要用小括号 () 包裹条件,用了反而会收到警告。

  • 分支:必须用大括号 {}。

  • 类型一致性:所有分支必须返回相同类型的值。


fn if_else_test() -> i32 {if 1 > 2 { // 条件无需括号1 // if 分支的值} else {2 // else 分支的值}// 整个 if-else 表达式的类型是 i32
}
  • 常见错误:遗漏 else 分支
fn if_missing_else() -> i32 {if true {1 // 类型是 i32}// 隐含的 else 分支返回 (),类型不匹配!}
  • 修复:确保所有可能路径都有返回且类型一致,或使用 return 提前返回。
  1. loop 循环
  • 无限循环。

  • break 可以跳出循环。

  • continue 跳过本次循环的剩余代码,进入下一次迭代。

  • 关键特性:loop 可以是一个表达式,break 后可以跟一个值作为整个 loop 表达式的返回值。

fn loop_expression() -> i32 {let mut counter = 0;let result = loop {counter += 1;if counter == 10 {break counter * 2; // break 返回值,整个 loop 的值就是 counter * 2}};result}
  1. loop vs while true
  • 语义差异:对编译器静态分析而言,loop {} 明确表示无限循环,而 while true {} 是一个条件循环。

  • 影响:编译器能更精确地分析 loop 块内的变量初始化情况。

let x;loop {x = 1;break;}println!("{}", x); // 编译成功!编译器知道 x 肯定被初始化了。let x;while true { // 编译器认为条件可能在运行时为 false(即使不会)x = 1;break;}println!("{}", x); // 编译错误!编译器认为 x 可能未初始化。
建议:需要无限循环时,优先使用 loop。
  1. while 循环
  • 条件循环。条件必须是 bool 类型。

  • 注意:while 循环本身不是表达式,其值永远是 ()。


fn while_test() {let mut n = 1;while n < 100 { // 条件无需括号// ... 循环体n += 1;}// while 循环的值是 ()
}
  1. for 循环
  • 用于迭代实现 Iterator trait 的集合(如数组、范围、Vec 等)。

  • 本质上是一种语法糖。

  • 不是表达式,其值为 ()。


fn for_test() {let array = [1, 2, 3, 4, 5];for element in array.iter() { // 使用迭代器println!("The number is {}", element);}// 等价写法:for element in &array { // 直接引用数组println!("The number is {}", element);}
}

七、常见错误总结

  1. 连续比较:

a == b == c // 错误!
// 应写成:a == b && b == c
  1. if 表达式遗漏 else 分支:

let n = if true { 10 }; // 错误!if 是 i32,隐含 else 是 ()
// 应补充 else 分支或使用其他控制流
  1. 误在表达式末尾加分号(尤其是在函数最后一行和块的最后一行):

fn foo() -> i32 {42; // 错误!变成了语句,返回 () 而非 i32
}
let x = { 42; }; // x 的类型是 (),而不是 i32
http://www.xdnf.cn/news/18998.html

相关文章:

  • Java HTTP 请求:Unirest 使用指南及与 HttpClient 对比
  • .Net Core Web 架构(Request Pipeline)的底层实现
  • 自己定义的模型如何用hf的from_pretrained
  • Linux(一) | 初识Linux与目录管理基础命令掌握
  • 测试题ansible临时命令模块
  • CuTe C++ 简介01,从示例开始
  • imx6ull-驱动开发篇47——Linux SPI 驱动实验
  • Electron解压缩文件
  • hive on tez为什么写表时,要写临时文件到hdfs目录
  • docker 1分钟 快速搭建 redis 哨兵集群
  • 配置nginx.conf (增加21001端口实例操作)
  • 医疗AI时代的生物医学Go编程:高性能计算与精准医疗的案例分析(三)
  • [灵动微电子 MM32BIN560CN MM32SPIN0280]读懂电机MCU之比较器
  • jQuery 从入门到实践:基础语法、事件与元素操作全解析
  • mac电脑双屏显示时程序坞跑到副屏的解决方法
  • 机器视觉学习-day10-图像添加水印
  • Mybatis 与 Springboot 集成过程详解
  • Kubernetes一EFK日志架构
  • Ovis2.5技术解密:原生分辨率与“反思模式”如何铸就新一代MLLM王者
  • 嵌入式学习日志————实验:串口发送串口发送+接收
  • 2025年渗透测试面试题总结-37(题目+回答)
  • 2024年06月 Python(三级)真题解析#中国电子学会#全国青少年软件编程等级考试
  • 零基础-力扣100题从易到难详解(持续更新1-10题)
  • 【链表 - LeetCode】25. K 个一组翻转链表
  • DAY 58 经典时序预测模型2
  • Kubernetes 的20 个核心命令分类详解
  • Linex系统网络管理(二)
  • 数据结构第8章 排序(竟成)
  • SqlHelper类库的使用方法
  • .NET周刊【8月第3期 2025-08-17】