Rust 学习笔记 - 流程控制 与 Range 类型

前言

任何一门编程语言几乎都脱离不了:变量、基本类型、函数、注释、循环、条件判断,这是一门编程语言的语法基础,只有当掌握这些基础语法及概念才能更好的学习 Rust。

条件判断

if 表达式

if 语句在其他语言中很常见,这里不再多做解释,看注释即可。

// 判断 n 是否小于 0
if n < 0 {
	// 当 n 小于 0 时执行这句
	print!("{} 是负数", n);
} else {
	// 否则执行这句
	print!("{} 是 0", n);
}

if 表达式也支持 if...else if...else 语句:

// 判断是否满足 if 条件1
if condition1 {
    // ...
} else if condition2 { // 当不满足 条件1 时,判断是否满足条件2
    // ...
} else { // 条件1 和 条件2 都不满足时
    // ...
}

在 Rust 中,没有像其他语言那样的三元表达式(三目运算符),但是在 Rust if 是一个表达式,这意味着可以在 let 语句中使用它来对变量进行条件赋值,从而达到类似三元表达式的效果。

let condition = true;
 // number 的类型必须明确,因此两个分支的返回值类型必须一致
let number = if condition { 5 } else { 6 };
println!("The value of number is: {}", number);

模式匹配(match 表达式)

虽然严格来说 match 并不是条件判断,但它常作为 Rust 的条件控制结构,其功能类似于其他语言中的 switch / case 语句。match 针对一个值执行模式匹配,并根据匹配到的模式执行相应的代码。

let value = some_val();

match value {
    1 => println!("one"),
    2 => println!("two"),
    3 => println!("three"),
    _ => println!("anything"), // _ 类似于其他语言中的 default,用来匹配所有其它情况
}

match 匹配必须是穷尽的(exhaustive),也就是说所有可能的值都必须被考虑。使用 _ 模式可以捕捉所有未显式处理的值。

match 守卫(match guards)允许你为同一个 match 分支添加额外的条件判断,这样就可以基于值的属性做更精细的控制。

match some_val {
    val if val < 5 => println!("less than five"),
    val => println!("{}", val),
}

循环语句

loop 循环

loop 关键字创建了一个无限循环,它会不断地重复代码块,直到明确地通过 break 关键字退出循环。

let mut counter = 0;
loop {
    counter += 1;
    if counter == 10 {
        break;
    }
};

loop 也能返回一个值,这个值是在 break 后跟着的表达式。

let mut counter = 0;
let result = loop {
    counter += 1;
    if counter == 10 {
        break counter * 2; // 当 counter 达到 10 时退出循环,并返回 counter 的两倍
    }
};
println!("The result is {}", result); // 输出:The result is 20

while 循环

while 循环在循环的每次迭代开始前检查条件表达式,只有当条件为真时才会执行循环体。如果条件为假,则退出循环。

let mut number = 3;
while number != 0 {
    println!("{}", number);
    number -= 1;
}

和其他语言不一样在,Rust 中没有 do...while 语句,如果想要实现类似的效果可以通过 loop + if 模拟。

let mut count = 0;

loop {
    count += 1;
    println!("这是第 {} 次迭代", count);

    // 判断条件,如果条件不满足,则退出循环
    if count >= 10 {
        break;
    }
}

for 循环

for 循环是开发中使用最频繁的循环,尤其是需要迭代一个序列或迭代器时。对于集合中的每个元素,它都会执行一次循环体。for 循环更加安全和高效,而且它不会产生越界的风险。

let a = [10, 20, 30, 40, 50];

for element in a.iter() {
    println!("the value is: {}", element);
}

for 语句的特点

Rust 语言中的 for 循环不支持传统的 C/C++ 风格的 for (initialization; condition; update) 循环。在 Rust 中,for 循环被设计成基于迭代器的,这意味着它是用来遍历迭代器中的元素。之所以这样设计,是为了更安全和直接地处理集合中的元素,而不需要像在 C/C++ 中那样手动管理索引。

如果你想要执行类似于 for (int i = 0; i < 10; i++) 的循环,Rust 提供了一个 Range 类型,可以通过这个类型的 .. 或 ..=(包含上界)运算符来创建。

Rust 的 for 循环进行迭代示例如下:

// 使用 `..` 创建一个左闭右开的范围(0 到 9)
for i in 0..10 {
    println!("Value of i is: {}", i);
}

// 使用 `..=` 创建一个闭区间的范围(0 到 10)
for i in 0..=10 {
    println!("Value of i is: {}", i);
}

注意:Range 类型产生的数值并不包括区间的上界,需要使用 ..= 语法才可以。当需要类似 C/C++ 风格的循环控制时,通常可以使用 Range 类型,这种方式也避免了因手动处理索引导致类似越界访问的错误。

Range 类型

在 Rust 中,Range 是一种迭代器类型,它生成一系列按顺序排列的数字。Range 通常用于 for 循环中,提供了一种简便的方式来执行重复操作一定次数的任务。

这里有两种最常用的 Range 类型:

  1. Range (start..end): 创建一个从 start 到 end(不包括)的迭代器,也就是左闭右开的区间。
  2. RangeInclusive (start..=end): 创建一个从 start 到 end(包括)的迭代器,闭区间,包含结束值。

创建 Range:

Range 通过使用两个点 .. 运算符来创建:

let a = 0..5; // 这将包括整数 0, 1, 2, 3, 4

for i in a {
    println!("{}", i); // 打印 0 到 4。
}

创建 RangeInclusive:

RangeInclusive 通过使用三个点 ..= 运算符来创建:

let b = 0..=5; // 这将包括整数 0, 1, 2, 3, 4, 5

for i in a {
    println!("{}", i); // 打印 0 到 5。
}

这两种类型都是迭代器,可以使用它们的 next 方法来逐个获取值,或者使用 for 循环来遍历这些值。

常用方法

Range 类型支持很多操作方法,下面列举几个常用的。

rev

rev 方法可以用来反转范围,创建一个新的迭代器,它会按照相反的顺序产生数值。

let range = (1..10).rev();
for num in range {
    println!("{num}"); // 将从 9 递减到 1
}
count

返回迭代器中剩余的元素数量。

let range_cnt = (1..100).count();
println!("{range_cnt}"); // 输出: 99
start_bound 和 end_bound

返回Range的起始边界和结束边界。返回的是一个Bound枚举,可以是Included(包含边界值)或Excluded(不包含边界值)。

let range = 1..100;
println!("{:?}", range.start_bound()); // 输出: Included(1)
println!("{:?}", range.end_bound());   // 输出: Excluded(100)
contains

判断一个指定的数值是否在Range内。如果是start..end类型的Range,会判断这个值是否大于等于start且小于end

let range = 1..100;
println!("{}", range.contains(&50));  // 输出: true
println!("{}", range.contains(&100)); // 输出: false
next

迭代Range并返回下一个值,如果迭代已经完成则返回None

let mut range = 1..3;
assert_eq!(range.next(), Some(1));
assert_eq!(range.next(), Some(2));
assert_eq!(range.next(), None);
last

返回Range中的最后一个数值,对 start..end 类型的Range来说,这个值是end - 1

let range = 1..100;
println!("{}", range.last().unwrap()); // 输出: 99
nth

获取迭代器中的第n个元素,并且将迭代器前进到此位置后面的元素。

let mut range = 1..100;
println!("{}", range.nth(49).unwrap()); // 输出: 50,即第50个元素
step_by

创建一个每n个数值产生一次的迭代器。

let range = (0..10).step_by(3);
for num in range {
    println!("{num}"); // 输出: 0, 3, 6, 9
}
其他

这个只列举了最常用的一些方法,Range 还支持很多其他的方法,此外,由于 Range 和 RangeInclusive 实现了 Iterator trait,因此提供了所有迭代器的标准方法,如 mapfilterfold, 和 collect 等。

结语

本章介绍了条件判断、循环语句以及 Range 类型,这是在程序开发中最常用的语句。在 Rust 中,不支持 do...whilefor ( i = 0; i <10; i++ )三元运算符 这类语法,但是可以用其他语法平替,这是从其他语言切换到 Rust 开发时需要注意的。

参考资料

  • Rust 圣经:https://doc.rust-lang.org/book/ch03-05-control-flow.html
  • Rust 实例:https://doc.rust-lang.org/rust-by-example/flow_control.html
  • Rust 标准库: https://doc.rust-lang.org/std/ops/struct.Range.html

你可能感兴趣的:(Rust,学习笔记,rust,学习,笔记,后端,开发语言)