From ec9d5751756316b0bee6f54d39c8809ba1e2ad35 Mon Sep 17 00:00:00 2001 From: pengmao Date: Thu, 12 Dec 2024 18:18:47 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=97=A5=E5=B8=B8=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/rust/advanced/closure.md | 166 ++++++++++++++++ docs/rust/advanced/deepType.md | 208 ++++++++++++++++++++ docs/rust/advanced/iterator.md | 332 ++++++++++++++++++++++++++++++++ docs/rust/advanced/lifeCycle.md | 119 ++++++++++++ 4 files changed, 825 insertions(+) create mode 100644 docs/rust/advanced/closure.md create mode 100644 docs/rust/advanced/deepType.md create mode 100644 docs/rust/advanced/iterator.md diff --git a/docs/rust/advanced/closure.md b/docs/rust/advanced/closure.md new file mode 100644 index 0000000..4c2fd9a --- /dev/null +++ b/docs/rust/advanced/closure.md @@ -0,0 +1,166 @@ +# 闭包 + +闭包是可以捕获其环境中变量的匿名函数。与普通函数不同,闭包可以访问其定义范围内的变量(甚至是从外部传入的变量),并且可以作为值传递、赋值和调用,闭包是Rust中非常强大的功能之一,被广泛应用于函数式编程模式、并发、事件处理等场景 + +## 定于 + +Rust中的闭包通过`|`语法定义,类似于其他语言中的匿名函数 + +```rust +|参数1, 参数2, ...| -> 返回类型 { + // 闭包体 +} + +``` + +闭包的语法于普通函数类似,但它没有显式的`fn`关键字,并且参数和返回类型可以根据上下文推断 + +```rust +fn main() { + let add = |a, b| a + b; // 闭包,捕获了外部的 `a` 和 `b` + let result = add(2, 3); // 调用闭包 + println!("Result: {}", result); // 输出: Result: 5 +} + +``` + +## 闭包捕获环境 + +闭包的一个重要特性是它们能够捕获定义它们时所在作用域中的变量(即它们的环境)。Rust提供了三种方式来捕获这些变量: + +- 按值捕获(`move`):闭包将环境变量的所有权移动到闭包内部 +- 按引用捕获:闭包通过引用捕获变量(默认情况下,如果闭包只读取变量,Rust会选择引用捕获) +- 按可变引用捕获:如果闭包需要修改捕获的变量,Rust会通过可变引用捕获它们 + +按值捕获 + +```rust +fn main() { + let x = 5; + let closure = move || { + println!("x: {}", x); // `x` 的所有权被转移到闭包内 + }; + closure(); // 使用闭包 + // println!("{}", x); // 编译错误,`x` 已被移动 +} + +``` + +不可变引用 + +```rust +fn main() { + let x = 5; + let closure = || { + println!("x: {}", x); // 捕获 `x` 的引用 + }; + closure(); // 使用闭包 +} + +``` + +可变引用 + +```rust +fn main() { + let mut x = 5; + let mut closure = || { + x += 1; // 闭包通过可变引用修改 `x` + }; + closure(); + println!("x: {}", x); // 输出: x: 6 +} + +``` + +## 类型推断 + +Rust中的闭包会根据捕获的变量和参数类型自动推断其类型,通常不需要显式地标注类型。Rust会根据闭包的使用上下文来推断类型 + +```rust +fn apply(f: F) +where + F: Fn() { + f(); // 调用闭包 +} + +fn main() { + let closure = || println!("Hello, world!"); // 闭包 + apply(closure); // 调用闭包作为参数传递 +} + +``` + +在这里,apply 函数接受一个闭包,它的类型通过泛型 F 被推断出来,闭包的类型是 Fn(),因为它不接受参数并且没有返回值。更多可看生命周期部分内容 + +## 闭包和`Fn`特征 + +闭包在Rust中时通过`Fn`、`FnMut`和`FnOnce`特征来区分的。这些特征定义了闭包如何捕获变量,以及如何调用 + +- Fn:表示闭包只读取环境中的变量,不修改它们,并且可以多次调用 +- FnMut:表示闭包可以修改捕获的环境变量,并且可以多次调用 +- FnOnce:表示闭包只能调用一次,因为它捕获了环境中的值并且可能移动了它们 + +`Fn`和`FnMut` + +```rust +fn apply_fn(mut f: F) +where + F: FnMut(), +{ + f(); // 调用闭包 +} + +fn main() { + let mut count = 0; + let mut closure = || { + count += 1; // 修改环境中的变量 + println!("Count: {}", count); + }; + + apply_fn(closure); // 传递 FnMut 闭包 +} + +``` + +`FnOnce` + +```rust +fn apply_once(f: F) +where + F: FnOnce(), +{ + f(); // 调用闭包 +} + +fn main() { + let move_closure = move || { + let x = 5; + println!("x: {}", x); // 闭包拥有 `x` 的所有权 + }; + + apply_once(move_closure); // 传递 FnOnce 闭包 +} + +``` + +## 闭包和函数的区别 + +闭包普通函数之间有一些关键的区别: + +- 闭包捕获环境:闭包可以捕获它所在的环境的变量,普通函数不能 +- 闭包时匿名的:闭包通常没有名字,可以被传递和存储作为值,而函数有一个固定的签名 +- 闭包的调用方式灵活:闭包可以在定义的地方直接调用,也可以作为参数传递给其他函数 + +## 使用场景 + +- 迭代器:闭包常常用作迭代器中的处理函数,例如`map`等 + + ```rust + let numbers = vec![1, 2, 3, 4, 5]; + let doubled: Vec = numbers.into_iter().map(|x| x * 2).collect(); + println!("{:?}", doubled); // 输出: [2, 4, 6, 8, 10] + ``` + +- 事件驱动编程:闭包可以用来处理异步事件或回调函数 +- 函数式编程:闭包时函数式编程的核心,常用于高阶函数和组合函数的实现 diff --git a/docs/rust/advanced/deepType.md b/docs/rust/advanced/deepType.md new file mode 100644 index 0000000..8ebc69a --- /dev/null +++ b/docs/rust/advanced/deepType.md @@ -0,0 +1,208 @@ +# 深入类型 + +## 类型转换 + +`From`特征,该特征用于实现一种类型到另一种类型的转换。它提供了一个方法`from()`,可以将一个值从一种类型转换为另一种类型。 + +```rust +pub trait From: Sized { + fn from(t: T) -> Self +} +``` + +使用: + +```rust +struct MyType(i32); + +// 示例:实现从 i32 到 f64 的转换 +impl From for f64 { + fn from(item: MyType) -> f64 { + item as f64 // 使用 `as` 关键字进行类型转换 + } +} + +fn main() { + let x: MyType = MyType(5); + let y: f64 = f64::from(x); // 使用 From::from() 方法进行类型转换 + println!("{}", y); // 输出: 5.0 +} + +``` + +- From转换时自动转换的、 +- 它不会出现转换失败的情况。你可以放心地使用它,因为 Rust 保证 From 是无损转换。 + +`Into` 特征是`From`的反向实现。它允许我们通过`.into()`方法将一种类型转换为另一种类型。由于`Into`和`From`是相对应的,因此,Rust会根据From特征的实现自动推导`Into`的转换 + +```rust +pub trait Into: Sized { + fn into(self) -> T +} +``` + +```rust +fn main() { + let x: i32 = 5; + let y: f64 = x.into(); // 使用 `.into()` 自动转换 + println!("{}", y); // 输出: 5.0 +} + +``` + +- Into转换时通过实现From特征来自动提供的,使用`.into()`方法 +- 如果From转换存在,Into转换也会自动提供 + +### TryFrom、TryInto + +`TryFrom`和`TryInto`是Rust中的两个特征,类似于`From`和`Into`,但是它们用于可能失败的类型转换。这些转换返回`Result`,从而允许我们处理可能得错误。 + +`TryFrom`用于定义一种类型到另一种类型的可能失败的转换。它的`try_from()`方法返回一个`Result`,如果转换成功,返回ok,失败err + +定义 + +```rust +pub trait TryFrom: Sized { + fn try_from(t: T) -> Result; + type Error; +} + +``` + +使用 + +```rust +use std::convert::TryFrom; + +#[derive(Debug)] +struct MyStruct { + value: i32, +} + +impl TryFrom for MyStruct { + type Error = &'static str; + + fn try_from(value: i32) -> Result { + if value > 0 { + Ok(MyStruct { value }) + } else { + Err("Value must be positive") + } + } +} + +fn main() { + let result: Result = MyStruct::try_from(10); + match result { + Ok(structure) => println!("Success: {:?}", structure), + Err(e) => println!("Error: {}", e), + } +} + +``` + +`TryInto`特征 + +`TryInto`是`TryFrom`的反向特征,它允许你将值转换成另一种类型。如果成功,它返回ok,失败则err + +```rust +use std::convert::TryInto; + +fn main() { + let x: i32 = -5; + let result: Result = x.try_into(); // 使用 `try_into()` 进行转换 + match result { + Ok(s) => println!("Converted: {:?}", s), + Err(e) => println!("Error: {}", e), + } +} + +``` + +### as 关键字 + +`Rust`提供了`as`关键字来进行类型转换。`as`通常用于执行显式类型转换,例如从较大的数值类到较小的数值类型,或者将某个类型强制转换为另一个类型。`as`转换可能会丢失精度或产生错误,因此需要特别注意 + +```rust +fn main() { + let a: f64 = 10.5; + let b: i32 = a as i32; // 使用 `as` 关键字将 f64 转换为 i32 + println!("{}", b); // 输出: 10 +} + +``` + +- 使用`as`转换时,如果转换的目标类型无法容纳源类型的所有值,可能会发生截断或溢出 +- `as`也可以用于转换原始指针、引用、枚举等 + +## 点操作符 + +点操作符`.`是一个非常重要的操作符,主要用于 访问结构体、枚举、元组、对象、方法、字段等。它可以用来执行多种功能,如访问字段、调用方法、进行成员解构等。点操作符通常用于 +获取一个对象或值的成员 + +### 点操作符的基本功能 + +- 访问结构体的字段:可以直接通过点操作符访问结构体的字段 +- 调用对象的方法:点操作符用于调用实现了特定方法的类型(如结构题、枚举、trait对象等)的方法 + +```rust +struct Person { + name: String, + age: u32, +} +impl Person { + fn greet(&self) { + println!("Hello, {}", self.name); + } +} +fn main() { + let person = Person { + name: String::from("Alice"), + age: 30, + }; + + println!("Name: {}", person.name); // 使用点操作符访问字段 + println!("Age: {}", person.age); // 使用点操作符访问字段 + person.greet(); // 使用点操作符调用方法 +} + +``` + +### 点操作符与引用和所有权的关系 + +1. 引用与点操作符 + +Rust中,方法通常通过`&self`或`&mut self`来接受引用。点操作符在调用方法时会根据需要自动传递引用,尤其是当我们调用某个类型的方法时,Rust会自动解引用 +`Box`或其他智能指针类型,以便调用方法。 + +**自动解引用** + +Rust自动为智能指针类型(如`Box`,`Rc`,`Arc`)执行解引用操作,使得我们可以像直接使用值类型一样使用智能指针。点操作符会通过 +解引用来调用方法,避免显式的解引用操作 + +```rust +use std::ops::Deref; + +struct Person { + name: String, +} + +impl Person { + fn greet(&self) { + println!("Hello, {}", self.name); + } +} + +fn main() { + let person = Box::new(Person { + name: String::from("Alice"), + }); + + person.greet(); // 自动解引用 Box,调用 greet 方法 +} + +``` + +上述例子中,`person`时`Box`类型,点操作符会自动解引用`Box`,调用`greet`方法,等价于`(*person).greet()` + +**所有权与点操作符** diff --git a/docs/rust/advanced/iterator.md b/docs/rust/advanced/iterator.md new file mode 100644 index 0000000..e1e855d --- /dev/null +++ b/docs/rust/advanced/iterator.md @@ -0,0 +1,332 @@ +# 迭代器 + +迭代器是Rust中一个非常强大的概念,它是用于按顺序访问集合中的元素的一种对象。迭代器提供了一种抽象,使得你可以在没有显式索引的情况下,逐个访问集合中的元素。Rust 的标准库为集合类型提供了迭代器的实现,这使得迭代器成为处理数据的一种高效、灵活和表达式简洁的方式。 + +## 基本概念 + +在Rust中,迭代器是实现了`Iterator`特征的类型。`Iterator`特征定义了迭代器的基本行为,主要包含两个核心的方法: + +- `next()`:放回迭代器的下一个元素。每次调用`next()`时,迭代器放回一个`Option`,如果没有更多的元素,则返回`None`,否则放回`Some(T)` +- `size_hint()`:提供一个迭代器中剩余元素的估计数量。 + +## 使用 + +一个迭代器的基本用法是通过`next()`方法逐个获取元素。如Vec和数组 + +```rust +fn main() { + let numbers = vec![1, 2, 3, 4, 5]; + let mut iter = numbers.into_iter(); // 创建迭代器 + + while let Some(number) = iter.next() { + println!("{}", number); // 按顺序打印每个元素 + } +} + +``` + +## 迭代器链 + +Rust的迭代器设计非常强大,它支持链式调用。你可以通过多次调用迭代器的方法组合来进行复杂的操作,例如过滤、映射、排序等 + +Rust的迭代器通常是惰性求值的,这意味着方法调用不会立即执行,而是放回一个新的迭代器,直到你最终调用`.collect()`、`.for_each()`或类型方法来触发计算 + +链式操作 + +```rust +fn main() { + let numbers = vec![1, 2, 3, 4, 5]; + let result: Vec = numbers.into_iter() + .filter(|&x| x % 2 == 0) // 过滤出偶数 + .map(|x| x * x) // 对偶数平方 + .collect(); // 收集结果到 Vec + + println!("{:?}", result); // 输出: [4, 16] +} + +``` + +在这个例子中,使用了链式调用来过滤偶数并将其平方。`filter`和`map`都是惰性求值操作,只有在最终调用`collect()`时才会真正执行 + +## 常用方法 + +1. map:将闭包应用于每个元素,返回一个新的迭代器,元素变换后的结果 + +```rust +let numbers = vec![1, 2, 3]; +let result: Vec = numbers.into_iter().map(|x| x * 2).collect(); +println!("{:?}", result); // 输出: [2, 4, 6] + +``` + +2. filter:根据给定的条件筛选元素,放回一个新的迭代器,包含所有符合条件的元素 + +```rust +let numbers = vec![1, 2, 3, 4, 5]; +let even_numbers: Vec = numbers.into_iter().filter(|&x| x % 2 == 0).collect(); +println!("{:?}", even_numbers); // 输出: [2, 4] + +``` + +3. fold:将迭代器的元素结合成一个值,通常用来做累加、累乘等操作,`fold`接受一个初始值和一个闭包,在每一步迭代中将元素与累积值进行操作 + +```rust +let numbers = vec![1, 2, 3, 4, 5]; +let sum: i32 = numbers.into_iter().fold(0, |acc, x| acc + x); +println!("{}", sum); // 输出: 15 + +``` + +4. for_each:对每个元素执行一个给定的操作,通常用于副作用 + +```rust +let numbers = vec![1, 2, 3]; +numbers.into_iter().for_each(|x| println!("{}", x)); // 输出: 1 2 3 + +``` + +5. take:从迭代器中获取前`n`个元素,返回一个新的迭代器 + +```rust +let numbers = vec![1, 2, 3, 4, 5]; +let first_three: Vec = numbers.into_iter().take(3).collect(); +println!("{:?}", first_three); // 输出: [1, 2, 3] + +``` + +6. skip:跳过迭代器的前`n`个元素 + +```rust +let numbers = vec![1, 2, 3, 4, 5]; +let skip_first_two: Vec = numbers.into_iter().skip(2).collect(); +println!("{:?}", skip_first_two); // 输出: [3, 4, 5] + +``` + +7. all 和 any:检查是否满足条件 + + - all:检查是否所有元素都满足条件 + - any:检查是否任何一个元素满足条件 + + ```rust + let numbers = vec![1, 2, 3, 4, 5]; + let all_even = numbers.iter().all(|&x| x % 2 == 0); + let any_even = numbers.iter().any(|&x| x % 2 == 0); + println!("All even: {}", all_even); // 输出: All even: false + println!("Any even: {}", any_even); // 输出: Any even: true + + ``` + +## 惰性和激活 + +Rust 的迭代器采用惰性求值的策略,意味着方法如 map()、filter() 等只会返回新的迭代器,它们不会立即执行操作。直到你调用诸如 collect()、for_each() 等方法时,才会激活并执行实际操作。 + +```rust +let numbers = vec![1, 2, 3, 4, 5]; +let iter = numbers.into_iter().map(|x| x * 2); // 这里只创建了一个新的迭代器 +// 此时 map 尚未执行 +let result: Vec = iter.collect(); // 执行 map 操作并收集结果 +println!("{:?}", result); // 输出: [2, 4, 6, 8, 10] + +``` + +## IntoIterator 特征 + +在`Rust`中,`IntoIterator`特征用于将一个集合类型(如`Vec`,`String`,`HashMap`等)转换成一个可以消耗其所有元素的迭代器。这个特征定义了如何将一个值转换为迭代器,从而可以通过`into_iter()`方法来获取该集合的迭代器。 + +`IntoIterator`特征有两个主要的实现: + +1. 对于拥有所有权的类型(`Vec`、`String`等),它允许通过调用`into_iter()`将集合中的元素转移到迭代器中,消耗集合本身 +2. 对于不可变的借用类型,它运行通过调用`into_iter()`获取一个迭代器,但迭代器的元素是不可变借用 + +### 定义 + +```rust +pub trait IntoIterator { + type Item; + type IntoIter: Iterator; + + fn into_iter(self) -> Self::IntoIter +} +``` + +- `Item`:迭代器返回的元素类型 +- `IntoIter`:实现了`Iterator`特征的迭代器类型 +- `into_iter()`:将值转化为迭代器的方法 + +### 激活 + +有以下三种可以激活它的方法,将其转换为迭代器 + +- into_iter 会夺走所有权 +- iter 是借用 +- iter_mut 是可变借用 + +## Iterator 特征 + +`Iterator`特征是用于迭代集合元素的特征,定义了一个对象如何按顺序返回集合中的每个元素。任何实现了`Iterator`特征的类型都可以通过调用`.next()`方法按顺序获取集合中的元素 + +### 实现 + +```rust +struct MyIterator { + current: i32, + end: i32 +} + +impl MyIterator { + fn new(start: i32, end: i32) -> Self { + MyIterator { current: start, end} + } +} + +impl Iterator for MyIterator { + type Item = i32; + + fn next(&mut self) -> Option { + if self.current < self.end { + let result = self.current; + self.current += 1; + Some(result) + } else { + None + } + } +} + +fn main() { + let iter = MyIterator::new(0, 3); + for num in iter { + println!("{}", num); + } +} + +``` + +在这个例子中 + +- `MyIterator`是一个自定义的迭代器,它从`current`开始迭代,直到`end` +- `Iterator`特征的实现中,`next()`方法会返回当前元素并将`current`增加,直到达到`end`时返回`None` + +### `Iterator`和`IntoIterator`对比 + +- `Iterator`是用来迭代集合的,它通过`next()`放回逐个放回元素 +- `IntoIterator`是用来将集合转换为迭代器的特征,它通过`into_iter()`方法把集合转换为一个消耗其所有权的迭代器 +- Iterator 是用于提供迭代能力的,而 IntoIterator 是用于提供转换能力的。 +- 要实现 Iterator,需要实现 next() 方法;而要实现 IntoIterator,需要实现 into_iter() 方法 + +`Iterator` 就是迭代器特征,只有实现了它才能称为迭代器,才能调用 `next`。而 `IntoIterator` 强调的是某一个类型如果实现了该特征,它可以通过 `into_iter`,`iter` 等方法变成一个迭代器。 + +## 消费者与适配器 + +### 消费者适配器 + +只要迭代器上的某个方法`A`在其内部调用了`next`方法,那么`A`就被称为消费性适配器:因为`next`方法会消耗掉迭代器上的元素,所有方法`A`的调用也会消耗掉迭代器上的元素 + +一个例子是`sum`方法,它会拿走迭代器的所有权,然后通过不断调用`next`方法对里面的元素进行求和 + +```rust +fn main() { + let v1 = vec![1, 2, 3]; + let v1_iter = v1.iter(); + let total: i32 = v1_iter.sum(); + assert_eq!(total, 6); + + // v1_iter 是借用了 v1,因此 v1 可以照常使用 + println!("{:?}",v1); + + // 以下代码会报错,因为 `sum` 拿到了迭代器 `v1_iter` 的所有权 + // println!("{:?}",v1_iter); +} +``` + +`sum` 源码 + +```rust +fn sum(self) -> S + where + Self: Sized, + S: Sum, + { + Sum::sum(self) + } + + +``` + +### 迭代器适配器 + +同消费者适配器对比,迭代器适配器是会返回一个新的迭代器 + +与消费者适配器不同,迭代器适配器是惰性的,意味着你需要一个消费者适配器来收尾,最终将迭代器转换成一个具体的值: + +```rust +let v1: Vec = vec![1, 2, 3]; + +let v2: Vec<_> = v1.iter().map(|x| x + 1).collect(); + +assert_eq!(v2, vec![2, 3, 4]); +``` + +#### collect + +https://fitgirl-repacks.site/dragon-ball-sparking-zero/#comment-6604653007 + +`collect`方法是一个消费者适配器,使用它可以将一个迭代器中的元素收集到指定类型中。在上面代码中,我们将v2标注了`Vec<_>`类型,就是为 了告诉`collect`:请把迭代器中的元素消费掉,让后把值收集成`Vec<_>`类型,使用`_`,编译器会帮我们自动推导其类型。 + +定义如下: + +```rust +pub fn collect(self) -> B +where + B: FromIterator, + +``` + +- self:迭代器本身 +- B:返回的目标类型,它 必须实现`FromIterator`,即可以从迭代器中收集元素并构造模板类型 + +`collect`的底层实现依赖于`FromIterator`特征。具体来说,当你调用`collect`时,它会根据目标类型来推到应该收集成什么类型,并将元素逐个添加到目标集合中。目标类型必须实现`FromIterator`,这意味着目标类型能够从一个迭代器中创建自身 + +`FromIterator`特征是用来指定如何从一个迭代器创建一个集合类型的。对于`Vec`,它的`FromIterator`实现会把迭代器中的每个元素推入`Vec`中 + +```rust +impl FromIterator for Vec { + fn from_iter>(iter: I) -> Self { + let mut vec = Vec::new(); + for item in iter { + vec.push(item); + } + vec + } +} + +``` + +显式指定类型: + +```rust +fn main() { + let numbers = vec![1, 2, 3, 4]; + let squared: Vec = numbers.into_iter() + .map(|x| x * x) + .collect(); // 显式指定收集成 Vec + + println!("{:?}", squared); // 输出: [1, 4, 9, 16] +} + +``` + +隐式指定类型 + +```rust +fn main() { + let numbers = vec![1, 2, 3, 4]; + let squared = numbers.into_iter() + .map(|x| x * x) + .collect::>(); // 使用类型注解指定目标类型 + + println!("{:?}", squared); // 输出: [1, 4, 9, 16] +} + +``` diff --git a/docs/rust/advanced/lifeCycle.md b/docs/rust/advanced/lifeCycle.md index 492c030..7fcc3cd 100644 --- a/docs/rust/advanced/lifeCycle.md +++ b/docs/rust/advanced/lifeCycle.md @@ -36,3 +36,122 @@ fn fn_elision<'a>(x: &'a i32) -> &'a i32 { x } 1. 闭包的参数生命周期 - 闭包的参数生命周期会由函数的类型决定。闭包的生命周期推断通常与函数类似,但闭包的生命周期时根据它的上下文来决定的 2. 闭包的返回值生命周期 + - 如果闭包返回一个引用,`Rust`会推断返回值的生命周期为与参数相同的生命周期,或者根据闭包捕获的环境来推断 +3. 闭包捕获环境 + - 闭包可以捕获它外部作用域中的变量。`Rust`会根据闭包如何捕获这些变量来决定生命周期 + - 按值捕获:如果闭包按值捕获外部变量(即`move`闭包),则这些变量的生命周期由闭包的生命周期来决定 + - 按引用捕获:如果闭包按引用捕获外部变量,则引用的生命周期必须与闭包的生命周期相适应 + +### 几个例子 + +1. 普通闭包 + +```rust +let closure = |x: &i32| -> &i332 { x } +``` + +这里,`x`是一个`&i32`类型的引用,闭包返回一个`&i32`类型的引用。`Rust`会推断出生命周期: + +```rust +let closure = |x: &'a i32| -> &'a i32 { x } +``` + +其中,`'a`时推断出来的生命周期,意味着闭包的输入和输出引用共享相同的生命周期。这个生命周期通常由闭包使用时的上下文(例如闭包被调用时的入参)来决定 + +2. 闭包捕获环境中的变量 + +考虑闭包捕获外部变量的例子: + +```rust +let y = 10; +let closure = |x: &i32| -> &i32 { + if x > &y { + &y + }else { + x + } +} +``` + +这里,闭包捕获了外部变量`y`,并返回一个`&i32`类型的引用。Rust会根据`y`和`x`的生命周期推断出闭包的生命周期。由于`y`是一个局部变量,它的生命周期只会在闭包被调用时才有效,因此Rust会确保返回的引用在闭包的生命周期内是有效的 + +3. `move`闭包 + +如果闭包使用`move`关键字捕获变量,闭包会拥有这些变量的所有权,而不是借用它们。这意味着闭包的生命周期和它捕获的变量的生命周期没有直接关系 + +```rust +let y = 10; +let closure = move |x: &i32| -> i32 { + x + y +} +``` + +上面的例子中,闭包通过`move`捕获了`y`的所有权,而不是借用它。`y`的生命周期与闭包的生命周期无关,因为闭包不再借用`y`,而是将`y`移入闭包中,闭包在自己的生命周期内拥有它。 + +4. 闭包的生命周期推断与函数类似 + +闭包的生命周期推断通常遵循和普通函数类似的规则。 + +```rust +fn call_closure<'a>(closure: &'a dyn Fn(&i32) -> &i32) -> &'a i32 { + let x = 5; + closure(&x) +} +``` + +这里,`closure`是一个接受`&i32`类型引用并返回`&i32`类型引用的闭包。Rust会根据`call_closure`的生命周期参数`'a`来推断闭包的生命周期,这样就保证了返回的引用和输入的引用生命周期一致 + +## &'static 和 T: 'static + +### &'static + +`&'static`表示一个引用,生命周期为`'static`,即该引用指向的数据在程序的整个生命周期内都是有效的 + +使用场景 + +- _静态数据_:指向静态变量或字符串字面量的引用 +- _永久有效的引用_:引用一些在程序执行期间始终存在的数据 + +```rust +static HELLO: &str = "Hello, world!"; // 静态变量,生命周期是 'static + +fn main() { + let s: &'static str = "Hello, world!"; // 字符串字面量,生命周期是 'static + println!("{}", s); +} + +``` + +在上面的例子中,`s`是一个`&'static str`类型的引用,指向字面量`"Hello, world!"`。字面量具有`'static`生命周期,意味着它会在整个程序的生命周期内都有效 + +### T: 'static + +`T: 'static`是一种泛型约束,用来表示类型`T`必须具有`'static`生命周期,即类型`T`包含的引用或数据的生命周期必须持续整个程序的生命周期 + +使用场景 + +- _泛型类型约束_:当你定义泛型函数或结构体时,可以使用`T: 'static`来要求传入的类型包含`'static`生命周期的数据 +- _确保数据的长期有效性_:确保类型`T`在程序的整个生命周期内都有效,通常用于跨线程的数据共享或确保引用数据永远不会过期 + +```rust +fn accept_static(data: T) { + // 这里的 T 必须是一个包含 'static 生命周期的类型 + println!("{:?}", data); +} + +fn main() { + accept_static("Hello, world!"); // 字符串字面量有 'static 生命周期 + // accept_static(String::from("Hello")); // 这会报错,因为 String 不是 'static +} + +``` + +在这个例子中,`T: 'static`限制了泛型参数`T`必须具有`'static`生命周期。具体来说,只有那些包含`'static`生命周期的类型才可以传递给`accept_static`函数。例如,字符串字面量`"Hello, world!"`是一个`'static`类型,因此可以传递给函数 + +但是,`String`类型的实例通常没有`'static`生命周期,除非它时显示的定义为`'static`(例如存储在全局常量中)。因此,如果传递了一个`String`实例,Rust会报错,指出它的生命周期不是`static` + +_总结_ + +- &'static 是一种引用类型,表示引用的生命周期为整个程序的运行期,即 'static。 +- T: 'static 是泛型约束,表示泛型类型 T 包含的数据必须在程序的整个生命周期内有效。 +- &'static 常用于表示静态数据(如字符串字面量、全局常量、静态变量等),而 T: 'static 常用于多线程或需要跨越较长时间的数据共享场景。