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中,所有的变量默认都是不可变的量,也就是常量
对于上面的代码,如果直接执行的话就会报错
这是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表示没有值
Rust 标准库中定义了:
enum Option<T> {
Some(T), // 表示有值,里面存着 T 类型的数据
None, // 表示没有值
}
而x.pop(),是Vec<T>的一个方法,它移除并返回最后一个元素
从图中的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 是变量名且未被绑定时),() (元组空值)。 |
所以上面的代码,执行起来就是这样的了
貌似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);
}
迭代器循环
句法
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
表达式的效果相同。
这一节的学习就先到这里了,挖了好几个坑,后面再来填