提交
This commit is contained in:
parent
eb8971767d
commit
097735c734
@ -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
|
||||
```
|
||||
|
||||
**&** 运算符可以取变量的"引用"。
|
||||
|
||||
当一个变量的值被引用时,变量本身不会被认定无效。因为"引用"并没有在栈中复制变量的值:
|
||||
|
||||

|
||||
|
||||
函数参数传递的道理一样:
|
||||
|
||||
```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);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user