文章目录
一、if流程控制与match模式匹配
1.流程控制
Execution Flow(流程)
- 1.代码执行从上倒下Iine-by-line
- 2.而我们执行操作时,控制流程可能会改变
主要的流程控制结构
1.顺序结构(Sequential Structure):程序按照代码的顺序一步一步执行,没有跳过或循环。
2.选择结构(Selection Structure):
-根据条件选择不同的路径执行。常见的选择结构有:
- if语句:根据条件执行不同的代码块。
- switchi语句(在某些编程语言中):根据不同的条件值执行不同的代码块。
3.循环结构(Iteration Structure):重复执行一段代码,直到满足某个条件为止。常见的循环结构有:
- for循环:按照指定的次数重复执行一段代码。
- while循环:在条件为真的情况下重复执行一段代码。
- do-while循环:类似于while循环,但是保证至少执行一次循环体。
4.跳转结构:控制程序的执行流程跳转到指定的位置。常见的跳转结构有:
- break语句:终止循环或switch语句的执行。
- continue语句:跳过当前循环中的剩余代码,进入下一次迭代。
- goto语句(在某些编程语言中):直接跳转到指定的标签处。
2. IF流程控制
代码一行行执行
执行流程,被If(else)改变
可以嵌套使用,但容易引起可读性问题
3.match 表达式
match
用于模式匹配,允许更复杂的条件和分支。
可以处理多个模式,提高代码的表达力。
·match
是表达式,可以返回值。
match value{
pattern1 =>/code block executed if value matches pattern1,
pattern2 if condition =>/code block executed if value matches pattern2 and condition is true,
_=>/code block executed for any other case,
}
IF流程控制与match表达式对比
复杂性:if
适用于简单的条件判断,而match
更适用于复杂的模式匹配。
表达力:match
更灵活,可以处理多个条件和模式,使代码更清晰。
返回值:两者都是表达式,可以返回值,但match
通常用于更复杂的场景。
fn main() {
let age = 50;
if age < 50 {
println!("You are young");
} else {
println!("You are old");
}
// if 的表达能力很弱
let scores = 70;
if scores > 90 {
println!("Good!!!");
} else if scores > 60 {
println!("You are OK!");
} else {
println!("Bad!!!");
}
// 类似C++ 三元表达式
let msg = if age > 50 { "old" } else { "young" };
println!("You are {msg}");
// match
let num = 90;
match num {
80 => println!("80"),
90 => println!("90"),
_ => println!("Some else"),
}
match num {
25..=50 => println!("25 ... 50"),
51..=100 => println!("51 ... 100"),
_ => println!("Some else"),
}
match num {
25 | 50 | 75 => print!("25 or 50 or 75"),
100 | 200 => println!("100 or 200"),
_ => println!("Some else"),
}
// match 里面使用if
match num {
x if x < 60 => println!("bad"),
x if x == 60 => println!("luck"),
_ => println!("Some else"),
}
let num = 60;
let res = match num {
x if x < 60 => "bad".to_owned(),
x if x == 60 => "luck".to_owned(),
_ => "Some else".to_owned(),
};
println!("res value : {res}");
}
编译及运行
cargo run
Compiling ch11_if_match v0.1.0 (/home/wangji/installer/rust/project/ch11_if_match)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 7.55s
Running `target/debug/ch11_if_match`
You are old
You are OK!
You are yong
二、循环与break continue以及与迭代的区别
1.Rust中的循环Loops
Rust提供了几种循环结构,其中最常见的是Ioop
、while
和for
。
- 1.Ioop循环:
loop{
///无限循环的代码块
}
Ioop
创建一个无限循环,可以通过break
语句来中断循环。
- 2.while循环:
while condition{
//条件为真时执行的代码块
}
while
循环在每次迭代之前检查一个条件,只有在条件为真时才执行循环体。
- 3.
for
循环:
for item in iterable
{
//遍历可迭代对象执行的代码块
}
for
循环用于迭代集合或范围,执行代码块来处理每个元素。
2.break && continue
·break关键字用于立即终止循环,并跳出循环体
- 可以用于跳出指定标签循环
·continue关键字用于立即跳过当前循环中剩余的代码,直接进入下一次循环
3.迭代
Rust的迭代主要通过迭代器(iterators)来实现。迭代器是一个抽象,它提供了一种访问集合元素的统一方式。
从实现上讲在Rust中,迭代器是一种实现了Iterator trait的类型!
简化源码:
pub trait lterator{
type Item;
fn next (&mut self)->Option<Self:Item>;
}
4.循环与迭代的不同
循环适用于需要明确控制循环流程的情况,而迭代器则提供了一种更抽象的方式来处理集合元素。通常,推荐使用迭代器,因为它们可以提高代码的可读性和表达力。
fo循环是一种语法结构,用于遍历集合中的元素。它依赖于集合类型实现Iterator trait.
- 在Rust中,迭代器提供了一系列用于遍历集合元素的方法,比如next()、mapO、filter)等,可以让我们的代码更具有表达性。
Example
fn main() {
// loop {
// println!("Ctrl+C");
// std::thread::sleep(std::time::Duration::from_secs(1));
// }
// while循环
let mut i = 0;
while i < 10 {
println!("{}", i);
i += 1;
}
println!("for");
// for 循环
let arr = [0, 1, 2, 3, 4, 5, 6, 7, 8];
for element in arr {
println!("{}", element);
}
// 打印不包括10
for i in 0..10 {
println!("{}", i);
}
// 打印包括10
for i in 0..=10 {
println!("{}", i);
}
// break
let arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
for element in arr {
if element == 10 {
break;
}
println!("{element}");
}
let arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
for element in arr {
if element == 10 {
continue;
}
println!("{element}");
}
'outer: loop {
println!("outer");
loop {
println!("inner");
// break;
break 'outer; //直接跳出外部的循环
}
}
// 循环的写法
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);
// 迭代的写法,其性能与循环的性能差不多
let numbers = [1, 2, 3, 4, 5].to_vec();
let iter_number: Vec<_> = numbers.iter().map(|&x| x * x).collect();
println!("iter : {:?}", iter_number);
}
编译及测试:
cargo run
Compiling ch12_loop_iter v0.1.0 (/home/wangji/installer/rust/project/ch12_loop_iter)
Building [ ] 0/1: ch12_loop_iter(bin)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 9.99s
Running `target/debug/ch12_loop_iter`
0
1
2
3
4
5
6
7
8
9
for
0
1
2
3
4
5
6
7
8
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
10
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
11
outer
inner
for : [1, 4, 9, 16, 25]
iter : [1, 4, 9, 16, 25]
三、函数基础与Copy值参数传递
1.函数的基础知识
1.函数的定义:在Rust中,你可以使用fn关键字声明和定义函数,而main是程序的入口点是一种特殊的函数。
2.参数和返回值
函数可以接受零个或多个参数,每个参数都需要指定类型。
函数可以有返回值,使用->
指定返回值类型。如果函数没有返回值,可以使用->()、或省略这部分。
3.调用函数:调用函数时,使用函数名和传递给函数的实际参数。
2.Copy by value
如果数据类型实现Copy特质,则在函数传参时回实现Copy by value操作。
会将实参拷贝为形参,参数改变并不会影响实参。
如果要改变形参,需要加上mut
Struct、枚举、集合等并没有实现copy trait,会实现move操作失去所有权
为数据类型实现copy trait,就可实现copy by value
Example:
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(Copy, Clone)]
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);
println!("x {x}");
modify_i32(&mut x); //变成可变引用
println!("x {x}");
let s = Point { x: 1, y: 2 };
print_point(s); // 由于打了标签#[derive(Copy, Clone)],所以可以直接传值
println!("{}", s.x);
}
编译及运行:
cargo run
Compiling ch13_fn v0.1.0 (/home/wangji/installer/rust/project/ch13_fn)
warning: field `y` is never read
--> src/main.rs:18:5
|
16 | struct Point {
| ----- field in this struct
17 | x: i32,
18 | y: i32,
| ^
|
= note: `Point` has a derived impl for the trait `Clone`, but this is intentionally ignored during dead code analysis
= note: `#[warn(dead_code)]` on by default
warning: `ch13_fn` (bin "ch13_fn") generated 1 warning
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.56s
Running `target/debug/ch13_fn`
c: 3
fn 5
x 1
x 5
point x 1
1
四、函数值参数的传递、不可变借用参数的传递、可变借用参数的传递
1.函数值参数传递(move)
函数的代码本身通常是存储在可执行文件的代码段,而在调用时函数会在栈,开辟一个新的stack frame(栈空间),用于存储函数的局部变量、参数和返回地址等信息,而当函数结束后会释放该空间。
而当传入non-Copy value(Vec、String等)传入函数时实参会转移value的所有权给形参,实参会失去value的所有权而在函数结束时,value的所有权会释放。
2.不可变借用(就是C++的引用)
如果你不想失去value的所有权,你又没有修改value的需求,你可以使用不可变借用
在Rust中,你可以将不可变引用作为函数的参数,从而在函数内部访问参数值但不能修改它。这有助于确保数据的安全性,防止在多处同时对数据进行写操作,从而避免数据竞争。
如何应用不可变借用
- Use*to deference,去获取其的值
3.可变借用
如果你有修改值的需求你可以使用可变借用,以允许在函数内部修改参数的值。这允许函数对参数进行写操作,但在同一时间内只能有一个可变引用。
需要在形参前加&mut
如何应用可变借用
- 同样使用Use*to deference,去获取其的值
Example:
// p1有默认拷贝
// p2没有默认拷贝,所以是move
fn move_func(p1: i32, p2: String) {
println!("p1 is {}", p1);
println!("p2 is {}", p2);
}
// borrow引用,但是在这里没意义,对于&i32这种类型而言
fn print_value(value: &i32) {
println!("{}", value);
}
fn string_func_borrow(s: &String) {
println!("{}-{}", (*s).to_uppercase(), s.to_uppercase());
}
#[derive(Debug)]
struct Point {
x: i32,
y: i32,
}
fn modify_point(point: &mut Point) {
(*point).x += 2;
point.y += 2; //等价于(*point).y += 2
}
fn main() {
let n = 12;
let s = String::from("oo");
move_func(n, s);
println!("n is {}", n);
// println!("s is {}", s);
let s = String::from("oo");
print_value(&n);
print_value(&n);
string_func_borrow(&s);
println!("s is {}", s);
let mut p = Point { x: 0, y: 0 };
println!("{:?}", p); //就是调用#[derive(Debug)]特质
modify_point(&mut p);
println!("{:?}", p);
}
编译及测试:
cargo run
Compiling ch14_fn_move_borrow_mut v0.1.0 (/home/wangji/installer/rust/project/ch14_fn_move_borrow_mut)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.28s
Running `target/debug/ch14_fn_move_borrow_mut`
p1 is 12
p2 is oo
n is 12
12
12
OO-OO
s is oo
Point { x: 0, y: 0 }
Point { x: 2, y: 2 }
五、函数返回值与所有权机制
1.返回Copy与Non-Copy
都可以返回,但是要注意Non-Copy是在堆上的。
性能:
- 在一般情况下,返回Copy类型的值通常具有更好的性能。这是因为Copy类型的值是通过复制进行返回的,而不涉及堆上内存的分配和释放,通常是在栈上分配。这样的操作比涉及堆上内存额分配和释放更为高效。