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 从迭代器中循环取值,直到迭代器为空

所有四种循环都支持 break ,continue 和 循环标签(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表达式只允许在循环体内使用,它有 break、break 'label 或(参见后面)break EXPR 或 break 'label EXPR 这四种形式。

break和loop返回值

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

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

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

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

 

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

 

上一篇 5、阶乘练习
下一篇 7、for循环

今日时光

00 : 00 :00
已过 0 天 剩余 0 天
0%