6、循环语句

循环语句

引入

while循环

在rust中,循环语句的格式和其他语言其实相差不大,格式如下

while <条件表达式>{
	
}

例如

let sum = 0;
let i = 1;
// "当i<=5时"
while i <= 5 {
    // `+=` is a shorthand for `sum = sum + i`
    sum += i;
    i += 1;
}

mut关键字

在rust中,所有的变量默认都是不可变的量,也就是常量

对于上面的代码,如果直接执行的话就会报错

image-20250829202756440

这是rust的一个设计理念,能减少错误的发生就尽一切可能减少错误的发生,防止变量因为某些意外的情况发生更改,确保代码执行效率

所以改为let mut sum=0; let mut i=1;即可运行

四大循环

Rust支持四种循环表达式

loop 无限循环
while 不断循环,直到谓词为假
while let 循环测试给定模式
for 从迭代器中循环取值,直到迭代器为空

所有四种循环都支持 breakcontinue循环标签(label)

只有loop循环支持对循环体 非平凡求值

无限循环

loop表达式可以不断重复内部的代码

loop{
	println!("Hello Rust");
}

没有包含关联的break表达式的loop表达式时发散的,并且具有类型“!”()这一块不太理解,!这个类型rust官方是有明确的解释的,先挖个坑,以后再来补充

谓词循环

while循环从对布尔型循环条件操作数求值开始。 如果循环条件操作数的求值结果为 true,则执行循环体块,然后控制流返回到循环条件操作数。如果循环条件操作数的求值结果为 false,则 while表达式完成。

let mut i = 0;

while i < 10 {
    println!("hello");
    i = i + 1;
}

这一块也没什么特别的,和常规的while表达式没什么大的区别

ps. 为什么这里叫他 谓词循环 呢?我以前很少听过这个词,查过资料后,大概可以明白,其实他就是控制循环执行的条件,一般来讲,在计算机科学当中,可以将谓词理解为bool类型的变量或表达式,起到一定的控制作用

谓词模式循环

句法

while let Pattern(模式) = Expression(表达式){
    
}

while let循环在语义上类似于 while循环,但它用 let关键字后紧跟着一个模式、一个 =、一个检验对象(scrutinee)表达式和一个块表达式,来替代原来的条件表达式。

let mut x = vec![1, 2, 3];

while let Some(y) = x.pop() {
    println!("y = {}", y);
}

while let _ = 5 {
    println!("不可反驳模式总是会匹配成功");
    break;
}

上面的代码我刚看到感觉有点难以理解,直接查资料解释一下吧

let mut x = vec![1,2,3];

vec![]是宏,能够自动推断出元素类型,所以一般不用写

写全了的话就是这个

let mut x:Vec<i32> = vec![1,2,3];

<i32>其实就类似于C++当中的模板(template)

关于Vec好像还可以这样构建

let mut x = Vec::new();
x.push(1);
x.push(2);
x.push(3);

等后面接触到再慢慢学习吧

Some(y)=x.pop()

这里就是谓词模式匹配当中的所谓的模式匹配了吧,首先看这个Some

Some是Option<T>枚举的一个变体,表示“有值”

相对应的None表示没有值

image-20250829214203431

Rust 标准库中定义了:

enum Option<T> {
    Some(T),   // 表示有值,里面存着 T 类型的数据
    None,      // 表示没有值
}

而x.pop(),是Vec<T>的一个方法,它移除并返回最后一个元素

image-20250829214332536

从图中的rust源代码也可以看出来,如果参数的len属性为0的话,就返回None,如果不为0,不管else代码块里写了啥,反正咱们能看到他最后返回了一个Some(),这就和前面对应起来了,确实可以这样比较欸

每次循环,都会进行这样一轮模式匹配,如果匹配成功,那么就执行循环体,如果匹配失败,那么就跳出循环体

不对,有点理解错误了,while let Some(y) = x.pop()这里面的y哪来的?

这里的 = 好像是个赋值语句?难道是,x.pop返回的值,如果是Some()的话,就把值提取出来,赋给y?

感觉语法有点奇怪啊

哦哦,问了一下AI,这里的 = 其实是模式绑定(pattern binding)。它的作用是:将右边的值 与左边的模式进行匹配,如果成功,则把值“提取出来”,并绑定到左边的变量名上。

写法 含义
let y = 5; 赋值:把整数 5 绑定给 y
while let Some(y) = x.pop() 匹配 + 解构:只当 x.pop() 返回 Some(…) 时,才把里面那个值绑定给 y

还有就是一开始我以为Some(y)是定义了一个临时变量以和右边的表达式进行匹配,其实他是一个模式(pattern),他表示:

  • 值必须是 Option<T> 类型;
  • 并且它的形式是 Some(...),而不是 None
  • 如果是 Some(v),就把这个 v 提取出来,并命名为 y

其实就相当于一个模式匹配(pattern matching)语法糖

loop {
    match x.pop() {
        Some(y) => {
            println!("y = {}", y);  // 执行这里
            continue;
        }
        None => break;  // 退出循环
    }
}

那按照上面的描述,这个谓词模式循环,其实就是x.pop()三次,每次pop出来的都是Some(),然后匹配到左边的模式了,里面的y就被赋了刚刚pop出来的值

当 x pop完的时候,出来的就是None了,匹配不到左边的模式,所以循环就结束了。

所以这里就是输出三行了啊,y=1,2,3这样子

while let _ = 5

那现在还有最后一个地方需要清楚一下

while let _ = 5 {
    println!("不可反驳模式总是会匹配成功");
    break;
}

这里的 _ 代表一个 通配符(wildcard pattern)

5等于左边的 _ 模式,所以可以匹配成功,还有一个术语不可反驳模式

这里的 _就是典型的不可反驳模式

可反驳模式(refutable pattern) 可能匹配失败的模式。例如:Some(x)None 就不匹配;x == 5 也不一定成功。用于 if let, while let 等场景。
不可反驳模式(irrefutable pattern) 一定能够匹配成功的模式。无论右边是什么值,它总是能“接住”。例如:_, x(当 x 是变量名且未被绑定时),()(元组空值)。

所以上面的代码,执行起来就是这样的了

image-20250829220643708

貌似rust对不可反驳模式的谓词模式匹配有警告

 

哦,官方文档也说了

while let循环等价于包含匹配(match)表达式的 loop表达式。 如下:

'label: while let PATS = EXPR {
    /* loop body */
}

等价于

'label: loop {
    match EXPR {
        PATS => { /* loop body */ },
        _ => break,
    }
}

可以使用操作符 | 指定多个模式。 这与匹配(match)表达式中的 | 具有相同的语义:

let mut vals = vec![2, 3, 1, 2, 2];
while let Some(v @ 1) | Some(v @ 2) = vals.pop() {
    // 打印 2, 2, 然后 1
    println!("{}", v);
}

image-20250829221128191

迭代器循环

句法

for Pattern in Expression(排除结构体表达式) BlockExpression

for表达式是一个用于在 std::iter::IntoIterator 的某个迭代器实现提供的元素上进行循环的语法结构。 如果迭代器生成一个值,该值将与此 for表达式提供的不可反驳型模式进行匹配,执行循环体,然后控制流返回到 for循环的头部。 如果迭代器为空了,则 for表达式执行完成。

//for循环遍历数组内容的示例:
let v = &["apples", "cake", "coffee"];

for text in v {
    println!("I like {}.", text);
}

//for循环遍历一个整数序列的例子:
let mut sum = 0;
for n in 1..11 {
    sum += n;
}
assert_eq!(sum, 55);

上面那一段话有点难以理解,等后面学得多了,我再回来深入研究,先挖个坑

for循环等价于后面的块表达式。

'label: for PATTERN in iter_expr {
    /* loop body */
}

等价于:

{
    let result = match IntoIterator::into_iter(iter_expr) {
        mut iter => 'label: loop {
            let mut next;
            match Iterator::next(&mut iter) {
                Option::Some(val) => next = val,
                Option::None => break,
            };
            let PATTERN = next;
            let () = { /* loop body */ };
        },
    };
    result
}
/*
这里的 IntoIterator、Iterator 和 Option 是标准库的程序项(standard library item),不是当前作用域中解析的的任何名称。 变量名 next、iter 和 val 也仅用于表述需要,实际上它们不是用户可以输入的名称。
*/

这一块有点意思,可能就是rust里面for的核心逻辑,涉及到一些底层的rust核心机制,挖个坑,以后再慢慢研究

循环标签

句法

LoopLable:
	LIFETIME_OR_LABEL:

这里的循环标签,类似于C/C++里面的goto语句的标签

以前写C/C++代码的时候,遇到深层嵌套循环时,如果想要break到某一个地方,会非常麻烦,这个时候真的很想用goto语句,但是又有一个无形的枷锁,阻止你使用goto语句,让人很难受,最后还是用了goto(

循环标签好像就很好的解决了这个问题

//标签被标记为循环表达式之前的生存期(标签)
'foo: loop { 
    break 'foo; 
}

'bar: while false {
    
}

'humbug: for _ in 0..0 {
    
}

用了这个之后,break和continue就可以退出此标签标记的循环层,或将控制流移动到这个循环层的头部

剩下的就是关于break,continue的用法了,这一块还挺简单的其实

break表达式只允许在循环体内使用,它有 breakbreak 'label 或(参见后面break EXPRbreak 'label EXPR 这四种形式。

breakloop返回值

loop循环其实可以有返回值,通过break来返回一个值作为loop循环的返回值

形如 break EXPRbreak 'label EXPREXPR 是一个表达式,它的结果被从 loop循环中返回)

如果 loop 有关联的 break,则不认为该循环是发散的,并且 loop表达式的类型必须与每个 break表达式的类型兼容。

其后不跟表达式的 break 被认为与后跟 ()break表达式的效果相同。

 

这一节的学习就先到这里了,挖了好几个坑,后面再来填

 

阅读剩余
THE END