核心概念
流程控制和函数
if & match
Flow Control
Execution Flow(流程)
- 代码执行从上到下
line-by-line - 而我们执行操作时,控制流程可能会改变
主要的流程控制结构
顺序结构:程序按照代码的顺序一步一步执行,没有跳过或循环。
选择结构:根据条件选择不同的路径执行。常见的选择结构有:
if语句:根据条件执行不同的代码块switch语句:根据不同的条件值执行不同的代码块
循环结构:重复执行一段代码,直到满足某个条件为止。常见的循环结构有:
for循环:按照指定的次数重复执行一段代码。while循环:在条件为真的情况下重复执行一段代码。do-while循环:类似于while循环,但是保证至少执行一次循环体。
跳转结构:控制程序的执行流程跳转到指定的位置。常见的跳转结构有:
break语句:终止循环或switch语句的执行。continue语句:跳过当前循环中的剩余代码,进入下一次迭代。goto语句:直接跳转到指定的标签处。
match Expression
match用于模式匹配,允许更复杂的条件和分支。- 可以处理多个模式,提高代码的表达力。
match是表达式,可以返回值。
match value {pattern1 => // code block executed if value matches pattern1pattern2 if condition => // code block executed if value matches pattern2 and condition is true_ => // code block executed for any other case}
Loop & break continue & Iteration
Loop
Rust提供了几种循环结构,其中最常见的是loop、while和for。
loop循环:
loop {// 无限循环的代码块}
loop创建一个无限循环,可以通过break语句来中断循环。
while循环:
while condition {// 条件为真时执行的代码块}
while循环在每次迭代之前检查一个条件,只有在条件为真时才执行循环体。
for循环:
for item in iterable {// 遍历可迭代对象执行的代码块}
for循环用于迭代集合或范围,执行代码块来处理每个元素。
for
Rust中的for用于遍历迭代器。这与Python一致。
for var in iterator {// code}
例:打印0~9。
for x in 0..10 {println!("{}", x);}
例:反序打印0~9。
for x in (0..10).rev() {println!("{}", x);}
在遍历的过程中,若需要获取索引值,只需要对集合调用enumerate()函数即可。
例:使用enumerate函数,打印索引值与数值。
for (i,j) in (5..10).enumerate() {println!("i = {} and j = {}", i, j);}// 打印输出:// i = 0 and j = 5// i = 1 and j = 6// i = 2 and j = 7// i = 3 and j = 8// i = 4 and j = 9
例:打印出 行号与行内容。
let lines = "Content of line oneContent of line twoContent of line threeContent of line four".lines();for (linenumber, line) in lines.enumerate() {println!("{}: {}", linenumber, line);}// 打印输出:// 0: Content of line one// 1: Content of line two// 2: Content of line three// 3: Content of line four
例:打印向量for_numbers。
let numbers = [1, 2, 3, 4, 5];let mut for_numbers = Vec::new();for &number in numbers.iter() {let item = number * number;for_numbers.push(item);}println!("for :{:?}", for_numbers);// [1, 4, 9, 16, 25]
while
Rust中的while与其他语言中的while一致。唯一需要注意的是,条件表达式不需要括号。
while expression {// code}
loop
Rust中的loop用于方便实现无限循环。
loop {// cide}
该段代码等价于:
while true {// code}
例:
let mut x = 5;let mut done = false;while !done {x += x - 3;println!("{}", x);if x % 5 == 0 {done = true;}}
break & continue
跳出循环:
break跳出当前循环。continue带到当前循环的下一次迭代。 例:
let mut x = 5;loop {x += x - 3;println!("{}", x);if x % 5 == 0 { break; }}
label
与Java中的label一致。label用于在循环中跳到指定位置。
'outer: for x in 0..10 {'inner: for y in 0..10 {if x % 2 == 0 { continue 'outer; } // continues the loop over xif y % 2 == 0 { continue 'inner; } // continues the loop over yprintln!("x: {}, y: {}", x, y);}}
Iteration
Rust的迭代主要通过迭代器(iterators)来实现。迭代器是一个抽象,它提供了一种访问集合元素的统一方式。
从实现上讲在Rust中,迭代器是一种实现了Iterator trait的类型。
简化源码:
pub trait Iterator {type Item;fn next(&mut self) -> Option<Self::Item>;}
例:打印向量iter_number。
let numbers = [1, 2, 3, 4, 5].to_vec();let iter_number: Vec<_> = numbers.iter().map(|&x|x*x).collect();println!("iter : {:?}", iter_number);
Difference between Loop & Iteration
循环适用于需要明确控制循环流程的情况,而迭代器则提供了一种更抽象的方式来处理集合元素。通常,推荐使用迭代器,因为它们可以提高代码的可读性和表达力。
for循环是一种语法结构,用于遍历集合中的元素。它依赖于集合类型实现Iterator trait。
在Rust中,迭代器提供了一系列用于遍历集合元素的方法。比如next()、map()、filter()等,可以让我们的代码更具有表达性。
Function & Copy Parameter Passing
Copy by value
- 如果数据类型实现Copy特质,则在函数传参时会实现Copy by value操作
- 会将实参拷贝为形参,形参改变并不会影响实参
- 如果要改变形参需要加
mut Struct、枚举、集合等并没有实现Copy trait,会实现move操作失去所有权- 为数据类型实现Copy trait,就可实现Copy by value
fn add(x: i32, y: i32) -> i32 {x + y}fn change_i32(mut x: i32) {x = x + 4;println!("fn {x}");}fn modify_i32(x: &mut i32) {*x += 4;}#[derive(Clone, Copy)]struct Point {x: i32,y: i32,}fn print_point(point: Point) {println!("point x: {}", point.x);}fn main() {let a = 1;let b = 2;let c = add(a, b);println!("c: {c}");let mut x = 1; // 实参change_i32(x); // fn 5println!("change x: {x}"); // change x: 1// 实参x和change_i32函数中的形参改变没有关系modify_i32(&mut x); // 可变借用println!("modify x: {x}"); // modify x: 5let s = Point{x: 1, y: 2};print_point(s); // 所有权消失println!("{}", s.x); // 实现Copy}
Borrowing for Functions
Function Parameter Passing
函数的代码本身通常是存储在可执行文件的代码段,而在调用时函数会在栈上开辟一个新的stack frame(栈空间),用于存储函数的局部变量、参数和返回地址等信息,而当函数结束后会释放该空间。
而当传入non-Copy value(Vec、String等)
- 传入函数时实参会转移
value的所有权给形参,实参会失去value的所有权 - 而在函数结束时,
value的所有权会释放
Immutable Borrowing
- 如果你不想失去
value的所有权,你又没有修改value的需求,你可以使用不可变借用 - 在Rust中,你可以将不可变引用作为函数的参数,从而在函数内部访问参数值但不能修改它。这有助于确保数据的安全性,防止在多处同时对数据进行写操作,从而避免数据竞争
- 如何应用不可变借用
- Use
*to dereference,去获取其的值
- Use
Mutable Borrowing
- 如果你有修改值的需求你可以使用可变借用,以允许在函数内部修改参数的值。这允许函数对参数进行写操作,但在同一时间内只能有一个可变引用。
- 需要在形参前加
&mut - 如何应用可变引用
- 同样使用Use
*to dereference,去获取其的值
- 同样使用Use
Function Value Returning & Ownership
Return Copy & Non-Copy
都可以返回,但是要注意Non-Copy是在堆上的。
性能:在一般情况下,返回Copy类型的值通常具有更好的性能。这是因为Copy类型的值是通过复制进行返回的,而不涉及堆上内存的分配和释放,通常是在栈上分配。这样的操作比涉及堆上内存的分配和释放更为高效。
Return a Reference
- 在只有传入一个引用参数,只有一个返回引用时,生命周期不需要声明
- 其他情况下需要声明引用的生命周期
- 慎用
'static
高阶函数 函数作为参数与返回值
Higher-Order Functions
高阶函数(Higher-Order Functions):Rust允许使用高阶函数,即函数可以作为参数传递给其他函数,或者函数可以返回其他函数。
高阶函数也是函数式编程的重要特性。
Higher-Order Functions & Collections
map函数:map函数可以用于对一个集合中的每个元素应用一个函数,并返回包含结果的新集合。filter函数:filter函数用于过滤集合中的元素,根据一个谓词函数的返回值。fold函数:fold函数(有时也称为reduce)可以用于迭代集合的每个元素,并将它们累积到一个单一的结果中。
例子:
fn func_twice(f: fn(i32) -> i32, x: i32) -> i32 {f(f(x))}fn mul(x: i32) -> i32 {x * x}fn add(x: i32) -> i32 {x + 10}fn main() {let result = func_twice(mul, 3);println!("{result}"); // 81let res = func_twice(add, 10);println!("{res}"); // 30// 数学计算let numbers = vec![1, 2, 3, 4, 5, 6, 7];let res: Vec<_> = numbers.iter().map(|&x| x + x).collect();println!("{:?}", res); // [2, 4, 6, 8, 10, 12, 14]// filterlet numbers = vec![1, 2, 3, 4, 5, 6, 7];let evens: Vec<_> = numbers.into_iter().filter(|&x| x % 2 == 0).collect();println!("{:?}", evens); // [2, 4, 6]// reducelet numbers = vec![1, 2, 3, 4, 5, 6, 7];let sum = numbers.iter().fold(0, |acc, &x|acc + x);println!("Sum: {sum}"); // Sum: 28}