This commit is contained in:
wenyongda 2025-03-25 16:37:48 +08:00
parent eb8971767d
commit 097735c734

View File

@ -474,6 +474,10 @@ Hello, another!
## 函数参数
```rust
fn <函数名> ( <参数> ) <函数体>
```
Rust 中定义函数如果需要具备参数必须声明参数名称和类型:
```rust
@ -531,6 +535,458 @@ fn main() {
}
```
# Rust 条件语句
Rust 中条件语句格式是这样的:
```rust
fn main() {
let number = 3;
if number < 5 {
println!("条件为 true");
} else {
println!("条件为 false");
}
}
```
上述程序中,条件表达式 number < 5 不需要用小括号包括注意不需要不是不允许但是 Rust 中的 if 不存在单语句不用加 {} 的规则不允许使用一个语句代替一个块尽管如此Rust还是支持传统 else-if 语法的
```rust
fn main() {
let a = 12;
let b;
if a > 0 {
b = 1;
}
else if a < 0 {
b = -1;
}
else {
b = 0;
}
println!("b is {}", b);
}
```
运行结果:
```rust
b is 1
```
Rust 中的条件表达式必须是 bool 类型,例如下面的程序是错误的:
```rust
fn main() {
let number = 3;
if number { *// 报错expected `bool`, found integerrustc(E0308)*
println!("Yes");
}
}
```
虽然 C/C++ 语言中的条件表达式用整数表示,非 0 即真,但这个规则在很多注重代码安全性的语言中是被禁止的。
结合之前章学习的函数体表达式我们加以联想:
```rust
if <condition> { block 1 } else { block 2 }
```
在 Rust 中我们可以使用 if-else 结构实现类似于三元条件运算表达式 **(A ? B : C)** 的效果:
```rust
fn main() {
let a = 3;
let number = if a > 0 { 1 } else { -1 };
println!("number 为 {}", number);
}
```
运行结果:
```
number 为 1
```
if 语句块的返回值也可以给 number 进行赋值
用 if 来赋值时,要保证每个分支返回的类型一样(这种说法也不完全准确),此处返回的 `5``6` 就是同一个类型,如果返回类型不一致就会报错
```rust
fn main() {
let condition = true;
let number = if condition {
5
} else {
6
};
println!("The value of number is: {}", number);
}
```
# Rust 循环
Rust 的循环结果设计也十分成熟。
## while 循环
while 循环是最典型的条件语句循环:
```rust
fn main() {
let mut number = 1;
while number != 4 {
println!("{}", number);
number += 1;
}
println!("EXIT");
}
```
运行结果:
```
1
2
3
EXIT
```
在 C 语言中 for 循环使用三元语句控制循环,但是 Rust 中没有这种用法,需要用 while 循环来代替:
C 语言
```c
int i;
for (i = 0; i < 10; i++) {
*// 循环体*
}
```
Rust
```rust
let mut i = 0;
while i < 10 {
*// 循环体*
i += 1;
}
```
## for 循环
for 循环是最常用的循环结构常用来遍历一个线性数据结构比如数组。for 循环遍历数组:
```rust
fn main() {
let a = [10, 20, 30, 40, 50];
for i in a.iter() {
println!("值为 : {}", i);
}
}
```
运行结果:
```
值为 : 10
值为 : 20
值为 : 30
值为 : 40
值为 : 50
```
这个程序中的 for 循环完成了对数组 a 的遍历。a.iter() 代表 a 的迭代器iterator在学习有关于对象的章节以前不做赘述。
当然for 循环其实是可以通过下标来访问数组的:
```rust
fn main() {
let a = [10, 20, 30, 40, 50];
for i in 0..5 {
println!("a[{}] = {}", i, a[i]);
}
}
```
运行结果:
```
a[0] = 10
a[1] = 20
a[2] = 30
a[3] = 40
a[4] = 50
```
## loop 循环
某个循环无法在开头和结尾判断是否继续进行循环,必须在循环体中间某处控制循环的进行。如果遇到这种情况,我们经常会在一个 while (true) 循环体里实现中途退出循环的操作。
Rust 语言有原生的无限循环结构 —— loop
```rust
fn main() {
let s = ['R', 'U', 'N', 'O', 'O', 'B'];
let mut i = 0;
loop {
let ch = s[i];
if ch == 'O' {
break;
}
println!("\'{}\'", ch);
i += 1;
}
}
```
运行结果:
```
'R'
'U'
'N'
```
loop 循环可以通过 break 关键字类似于 return 一样使整个循环退出并给予外部一个返回值。这是一个十分巧妙的设计,因为 loop 这样的循环常被用来当作查找工具使用,如果找到了某个东西当然要将这个结果交出去:
```rust
fn main() {
let s = ['R', 'U', 'N', 'O', 'O', 'B'];
let mut i = 0;
let location = loop {
let ch = s[i];
if ch == 'O' {
break i;
}
i += 1;
};
println!(" \'O\' 的索引为 {}", location);
}
```
运行结果:
```
'O' 的索引为 3
```
# Rust 闭包
Rust 中的闭包是一种匿名函数,它们可以捕获并存储其环境中的变量。
闭包允许在其定义的作用域之外访问变量,并且可以在需要时将其移动或借用给闭包。
闭包在 Rust 中被广泛应用于函数编程、并发编程、和事件驱动编程等领域。
闭包在 Rust 中非常有用,因为它们提供了一种简洁的方式来编写和使用函数。
闭包在 Rust 中非常灵活,可以存储在变量中、作为参数传递,甚至作为返回值。
闭包通常用于需要短小的自定义逻辑的场景,例如迭代器、回调函数等。
## 闭包与函数的区别
| 特性 | 闭包 | 函数 |
| :------------- | :------------------------- | :--------------- |
| **匿名性** | 是匿名的,可存储为变量 | 有固定名称 |
| **环境捕获** | 可以捕获外部变量 | 不能捕获外部变量 |
| **定义方式** | ` | 参数 |
| **类型推导** | 参数和返回值类型可以推导 | 必须显式指定 |
| **存储与传递** | 可以作为变量、参数、返回值 | 同样支持 |
以下是 Rust 闭包的一些关键特性和用法:
## 闭包的声明
闭包的语法声明:
```rust
let closure_name = |参数列表| 表达式或语句块;
```
参数可以有类型注解也可以省略Rust 编译器会根据上下文推断它们。
```rust
let add_one = |x: i32| x + 1;
```
**闭包的参数和返回值:** 闭包可以有零个或多个参数,并且可以返回一个值。
```rust
let calculate = |a, b, c| a * b + c;
```
**闭包的调用:**闭包可以像函数一样被调用。
```rust
let result = calculate(1, 2, 3);
```
## 匿名函数
闭包在 Rust 中类似于匿名函数,可以在代码中以 **{}** 语法块的形式定义,使用 **||** 符号来表示参数列表,实例如下:
```rust
let add = |a, b| a + b;
println!("{}", add(2, 3)); // 输出: 5
```
在这个示例中add 是一个闭包,接受两个参数 a 和 b返回它们的和。
## 捕获外部变量
闭包可以捕获周围环境中的变量,这意味着它可以访问定义闭包时所在作用域中的变量。例如:
```rust
let x = 5;
let square = |num| num * x;
println!("{}", square(3)); // 输出: 15
```
以上代码中,闭包 square 捕获了外部变量 x并在闭包体中使用了它。
闭包可以通过三种方式捕获外部变量:
- **按引用捕获**(默认行为,类似 `&T`
- **按值捕获**(类似 `T`
- **可变借用捕获**(类似 `&mut T`
```rust
fn main() {
let mut num = 5;
// 按引用捕获
let print_num = || println!("num = {}", num);
print_num(); // 输出: num = 5
// 按值捕获
let take_num = move || println!("num taken = {}", num);
take_num(); // 输出: num taken = 5
// println!("{}", num); // 若取消注释将报错num 所有权被转移
// 可变借用捕获
let mut change_num = || num += 1;
change_num();
println!("num after closure = {}", num); // 输出: num after closure = 6
}
```
**说明:**
- 闭包默认按引用捕获外部变量。
- 使用 `move` 关键字可以强制按值捕获,将外部变量的所有权转移到闭包内。
- 如果闭包需要修改外部变量,需显式声明为 `mut` 闭包。
## 移动与借用
闭包可以通过 **move** 关键字获取外部变量的所有权,或者通过借用的方式获取外部变量的引用。例如:
**借用变量:**默认情况下,闭包会借用它捕获的环境中的变量,这意味着闭包可以使用这些变量,但不能改变它们的所有权。这种情况下,闭包和外部作用域都可以使用这些变量。例如:
```rust
let x = 10;
let add_x = |y| x + y;
println!("{}", add_x(5)); // 输出 15
println!("{}", x); // 仍然可以使用 x
```
**获取所有权:**通过在闭包前添加 move 关键字,闭包会获取它捕获的环境变量的所有权。这意味着这些变量的所有权会从外部作用域转移到闭包内部,外部作用域将无法再使用这些变量。例如:
```rust
let s = String::from("hello");
let print_s = move || println!("{}", s);
print_s(); // 输出 "hello"
// println!("{}", s); // 这行代码将会报错,因为 s 的所有权已经被转移给了闭包
```
通过这两种方式Rust 提供了灵活的机制来处理闭包与外部变量之间的关系,使得在编写并发、安全的代码时更加方便。
## 闭包的特性
### 闭包可以作为函数参数
闭包经常作为参数传递给函数,例如迭代器的 .map()、.filter() 方法:
```rust
fn apply_to_value<F>(val: i32, f: F) -> i32
where
F: Fn(i32) -> i32,
{
f(val)
}
fn main() {
let double = |x| x * 2;
let result = apply_to_value(5, double);
println!("Result: {}", result); // 输出: Result: 10
}
```
这里的 Fn 是闭包的一个特性trait用于表示闭包可以被调用。
### 闭包可以作为返回值
闭包还可以作为函数的返回值。由于闭包是匿名的,我们需要使用 impl Trait 或 Box 来描述其类型。
使用 impl Fn 返回闭包
```rust
fn make_adder(x: i32) -> impl Fn(i32) -> i32 {
move |y| x + y
}
fn main() {
let add_five = make_adder(5);
println!("5 + 3 = {}", add_five(3)); // 输出: 5 + 3 = 8
}
```
使用 `Box<dyn Fn>` 返回闭包
```rust
fn make_adder(x: i32) -> Box<dyn Fn(i32) -> i32> {
Box::new(move |y| x + y)
}
fn main() {
let add_ten = make_adder(10);
println!("10 + 2 = {}", add_ten(2)); // 输出: 10 + 2 = 12
}
```
### 闭包特性Traits
闭包根据其捕获方式自动实现了以下三个特性:
- **`Fn`**: 不需要修改捕获的变量,闭包可以多次调用。
- **`FnMut`**: 需要修改捕获的变量,闭包可以多次调用。
- **`FnOnce`**: 只需要捕获所有权,闭包只能调用一次。
```rust
fn call_closure<F>(f: F)
where
F: FnOnce(),
{
f(); // 只调用一次
}
fn main() {
let name = String::from("Rust");
// 使用 move 强制捕获所有权
let print_name = move || println!("Hello, {}!", name);
call_closure(print_name);
// println!("{}", name); // 若取消注释将报错name 的所有权已被移动
}
```
# Rust 所有权
计算机程序必须在运行时管理它们所使用的内存资源。
@ -699,3 +1155,126 @@ fn makes_copy(some_integer: i32) {
```
如果将变量当作参数传入函数,那么它和移动的效果是一样的。
### 函数返回值的所有权机制
```rust
fn main() {
let s1 = gives_ownership();
// gives_ownership 移动它的返回值到 s1
let s2 = String::from("hello");
// s2 被声明有效
let s3 = takes_and_gives_back(s2);
// s2 被当作参数移动, s3 获得返回值所有权
} // s3 无效被释放, s2 被移动, s1 无效被释放.
fn gives_ownership() -> String {
let some_string = String::from("hello");
// some_string 被声明有效
return some_string;
// some_string 被当作返回值移动出函数
}
fn takes_and_gives_back(a_string: String) -> String {
// a_string 被声明有效
a_string // a_string 被当作返回值移出函数
}
```
## 引用与租借
引用Reference是 C++ 开发者较为熟悉的概念。
如果你熟悉指针的概念,你可以把它看作一种指针。
实质上"引用"是变量的间接访问方式。
```rust
fn main() {
let s1 = String::from("hello");
let s2 = &s1;
println!("s1 is {}, s2 is {}", s1, s2);
}
```
运行结果:
```
s1 is hello, s2 is hello
```
**&** 运算符可以取变量的"引用"。
当一个变量的值被引用时,变量本身不会被认定无效。因为"引用"并没有在栈中复制变量的值:
![img](https://www.runoob.com/wp-content/uploads/2020/04/F25111E7-C5D3-464A-805D-D2186A30C8A0.jpg)
函数参数传递的道理一样:
```rust
fn main() {
let s1 = String::from("hello");
let len = calculate_length(&s1);
println!("The length of '{}' is {}.", s1, len);
}
fn calculate_length(s: &String) -> usize {
s.len()
}
```
运行结果:
```
The length of 'hello' is 5.
```
引用不会获得值的所有权。
引用只能租借Borrow值的所有权。
引用本身也是一个类型并具有一个值,这个值记录的是别的值所在的位置,但引用不具有所指值的所有权:
```rust
fn main() {
let s1 = String::from("hello");
let s2 = &s1;
let s3 = s1;
println!("{}", s2);
}
```
这段程序不正确:因为 s2 租借的 s1 已经将所有权移动到 s3所以 s2 将无法继续租借使用 s1 的所有权。如果需要使用 s2 使用该值,必须重新租借:
```rust
fn main() {
let s1 = String::from("hello");
let mut s2 = &s1;
let s3 = s1;
s2 = &s3; // 重新从 s3 租借所有权
println!("{}", s2);
}
```
这段程序是正确的。
既然引用不具有所有权,即使它租借了所有权,它也只享有使用权(这跟租房子是一个道理)。
如果尝试利用租借来的权利来修改数据会被阻止:
```rust
fn main() {
let s1 = String::from("run");
let s2 = &s1;
println!("{}", s2);
s2.push_str("oob"); // 错误,禁止修改租借的值
println!("{}", s2);
}
```