核心trait
标记 trait 是一种不包含任何方法的 trait,用于标记或标识某种特性。它们在 Rust 中用于为类型添加元数据或行为约束。
在 Rust 中,Send 是一个标记 trait,用于标记一个类型是否可以在线程之间安全地传递。实现了 Send trait 的类型意味着该类型的值可以安全地从一个线程移动到另一个线程,而不会引起数据竞争或其他并发问题。
Send 的定义
Section titled “Send 的定义”Send trait 的定义非常简单,它不包含任何方法,只是一个标记:
unsafe auto trait Send {}这个定义表示 Send 是一个自动实现的 trait,编译器会根据类型的内容自动决定类型是否实现 Send。通常,所有的基本类型和大多数标准库类型都实现了 Send。
Send 的自动实现
Section titled “Send 的自动实现”以下类型默认实现了 Send:
- 所有基本类型,例如
i32、f64、bool等。 - 标准库中的大多数类型,例如
String、Vec<T>、Box<T>等。 - 所有实现了
Send的类型的组合,例如包含Send成员的结构体和枚举。
以下类型默认不实现 Send:
- 指针。
- 非线程安全的引用计数类型
Rc<T>。 - 包含了
!Send类型的组合类型。
如何检查一个类型是否实现了 Send
Section titled “如何检查一个类型是否实现了 Send”你可以使用 Rust 的类型系统来检查一个类型是否实现了 Send。以下是一个示例,展示如何使用静态断言来检查类型是否实现了 Send:
use std::rc::Rc;use std::sync::Arc;
fn is_send<T: Send>() {}
fn main() { // 这些类型实现了 `Send` is_send::<i32>(); is_send::<String>(); is_send::<Vec<i32>>(); is_send::<Arc<i32>>();
// 这些类型不实现 `Send` // is_send::<Rc<i32>>(); // 取消注释会导致编译错误}在这个示例中,is_send 函数是一个泛型函数,它只有在泛型类型 T 实现了 Send 时才会编译通过。
Send 和多线程编程
Section titled “Send 和多线程编程”Send 在多线程编程中非常重要,因为它确保了在线程间传递数据的安全性。以下是一个使用 Send 的多线程示例:
use std::thread;
fn main() { let v = vec![1, 2, 3];
let handle = thread::spawn(move || { println!("Here's a vector: {:?}", v); });
handle.join().unwrap();}在这个示例中,v 被移动到子线程中并打印。由于 Vec<T> 实现了 Send,所以它可以安全地从主线程移动到子线程。
自定义类型实现 Send
Section titled “自定义类型实现 Send”通常情况下,你不需要手动实现 Send,因为编译器会自动为你做这些工作。然而,如果你有一个自定义类型并且需要确保它实现 Send,你可以显式地实现 Send。需要注意的是,手动实现 Send 通常涉及到 unsafe 代码。
struct MyStruct { data: *const u8,}
// 手动实现 `Send`unsafe impl Send for MyStruct {}
fn main() { let my_struct = MyStruct { data: std::ptr::null() };
let handle = thread::spawn(move || { println!("MyStruct is sent to another thread."); });
handle.join().unwrap();}在 Rust 中,Sync 是一个标记 trait,用于标记一个类型是否可以安全地在多个线程中共享引用。实现了 Sync trait 的类型意味着其引用可以安全地在多个线程中同时使用,而不会引起数据竞争或其他并发问题。
Sync 的定义
Section titled “Sync 的定义”Sync trait 的定义非常简单,它不包含任何方法,只是一个标记:
unsafe auto trait Sync {}这个定义表示 Sync 是一个自动实现的 trait,编译器会根据类型的内容自动决定类型是否实现 Sync。通常,所有线程安全的类型和大多数标准库类型都实现了 Sync。
Sync 的自动实现
Section titled “Sync 的自动实现”以下类型默认实现了 Sync:
- 所有不可变的基本类型,例如
i32、f64、bool等。 - 线程安全的智能指针类型,例如
Arc<T>。 - 包含
Sync成员的组合类型,例如结构体和枚举。
以下类型默认不实现 Sync:
- 指针。
- 非线程安全的引用计数类型
Rc<T>。 - 包含了
!Sync类型的组合类型。
如何检查一个类型是否实现了 Sync
Section titled “如何检查一个类型是否实现了 Sync”你可以使用 Rust 的类型系统来检查一个类型是否实现了 Sync。以下是一个示例,展示如何使用静态断言来检查类型是否实现了 Sync:
use std::rc::Rc;use std::sync::Arc;
fn is_sync<T: Sync>() {}
fn main() { // 这些类型实现了 `Sync` is_sync::<i32>(); is_sync::<String>(); is_sync::<Vec<i32>>(); is_sync::<Arc<i32>>();
// 这些类型不实现 `Sync` // is_sync::<Rc<i32>>(); // 取消注释会导致编译错误}在这个示例中,is_sync 函数是一个泛型函数,它只有在泛型类型 T 实现了 Sync 时才会编译通过。
Sync 和多线程编程
Section titled “Sync 和多线程编程”Sync 在多线程编程中非常重要,因为它确保了在多个线程中共享数据的安全性。以下是一个使用 Sync 的多线程示例:
use std::sync::Arc;use std::thread;
fn main() { let v = Arc::new(vec![1, 2, 3]);
let mut handles = vec![];
for _ in 0..10 { let v = Arc::clone(&v); let handle = thread::spawn(move || { println!("{:?}", v); }); handles.push(handle); }
for handle in handles { handle.join().unwrap(); }}在这个示例中,Arc<T> 是一个线程安全的引用计数类型,实现了 Sync,因此可以安全地在多个线程中共享。
自定义类型实现 Sync
Section titled “自定义类型实现 Sync”通常情况下,你不需要手动实现 Sync,因为编译器会自动为你做这些工作。然而,如果你有一个自定义类型并且需要确保它实现 Sync,你可以显式地实现 Sync。需要注意的是,手动实现 Sync 通常涉及到 unsafe 代码。
struct MyStruct { data: *const u8,}
// 手动实现 `Sync`unsafe impl Sync for MyStruct {}
fn main() { let my_struct = MyStruct { data: std::ptr::null() };
let handle1 = thread::spawn(move || { println!("MyStruct is accessed from thread 1."); });
let handle2 = thread::spawn(move || { println!("MyStruct is accessed from thread 2."); });
handle1.join().unwrap(); handle2.join().unwrap();}在 Rust 中,Unpin 是一个标记 trait,用于表示某个类型可以安全地移动。对于大多数类型,它们在内存中的位置并不重要,可以在内存中自由移动。然而,对于某些类型(特别是那些包含自引用或需要固定内存位置的类型),移动它们可能会导致内存安全问题。这些类型不会自动实现 Unpin。
Unpin 的定义
Section titled “Unpin 的定义”Unpin 是一个标记 trait,其定义非常简单,不包含任何方法:
pub auto trait Unpin {}这个定义表示 Unpin 是一个自动实现的 trait,编译器会根据类型的内容自动决定类型是否实现 Unpin。如果一个类型实现了 Unpin,那么它可以在内存中安全地移动。
Unpin 的自动实现
Section titled “Unpin 的自动实现”以下类型默认实现了 Unpin:
- 所有基本类型,例如
i32、f64、bool等。 - 大多数标准库类型,例如
String、Vec<T>、Box<T>等。 - 组合类型(如结构体和枚举),只要其所有字段都实现了
Unpin。
以下类型默认不实现 Unpin(即实现了!Unpin):
- 指针。
- async块
- async函数返回值
- 包含自引用的类型。
- 明确使用
PhantomPinned防止自动实现Unpin的类型。
如何检查一个类型是否实现了 Unpin
Section titled “如何检查一个类型是否实现了 Unpin”你可以使用 Rust 的类型系统来检查一个类型是否实现了 Unpin。以下是一个示例,展示如何使用静态断言来检查类型是否实现了 Unpin:
use std::marker::Unpin;
fn is_unpin<T: Unpin>() {}
fn main() { // 这些类型实现了 `Unpin` is_unpin::<i32>(); is_unpin::<String>(); is_unpin::<Vec<i32>>(); is_unpin::<Box<i32>>();
// 这些类型不实现 `Unpin` // is_unpin::<SelfReferentialStruct>(); // 假设 SelfReferentialStruct 没有实现 Unpin}在这个示例中,is_unpin 函数是一个泛型函数,它只有在泛型类型 T 实现了 Unpin 时才会编译通过。
自引用类型和 Unpin
Section titled “自引用类型和 Unpin”自引用类型通常不会自动实现 Unpin,因为它们需要固定的内存位置。例如:
use std::pin::Pin;use std::marker::PhantomPinned;
struct SelfReferential { data: String, pointer: *const String, _marker: PhantomPinned,}
impl SelfReferential { fn new(data: String) -> Self { Self { data, pointer: std::ptr::null(), _marker: PhantomPinned, } }
fn init_pointer(self: Pin<&mut Self>) { let self_ptr: *const String = &self.data; let this = unsafe { self.get_unchecked_mut() }; this.pointer = self_ptr; }}
fn main() { let mut instance = SelfReferential::new(String::from("hello")); let mut pinned_instance = Box::pin(instance);
pinned_instance.as_mut().init_pointer();
println!("{}", pinned_instance.data);}在这个示例中,SelfReferential 结构体包含一个指向自身数据的指针,因此需要确保其内存地址不变。使用 PhantomPinned 防止类型实现 Unpin。
手动实现 Unpin
Section titled “手动实现 Unpin”如果你确定某个类型可以安全地移动,即使它包含某些特殊的字段,你可以手动为其实现 Unpin。需要注意的是,手动实现 Unpin 通常涉及到 unsafe 代码。
use std::marker::Unpin;
struct MyStruct { data: *const u8,}
// 手动实现 `Unpin`unsafe impl Unpin for MyStruct {}
fn main() { let my_struct = MyStruct { data: std::ptr::null() };}在 Rust 中,Sized 是一个标记 trait,用于表示一个类型的大小在编译时是已知的。默认情况下,Rust 假设所有类型都是 Sized,这意味着类型的大小在编译时是固定的,可以被准确地计算和管理。
Sized 的定义
Section titled “Sized 的定义”Sized trait 的定义非常简单:
pub trait Sized {}Sized 的默认实现
Section titled “Sized 的默认实现”大多数类型默认实现了 Sized,包括:
- 所有基本类型,例如
i32、f64、bool等。 - 结构体、枚举和元组,只要它们的所有字段或变体都是
Sized。
以下是一些默认实现了 Sized 的类型示例:
fn main() { let x: i32 = 5; // `i32` 是 `Sized` let y: f64 = 3.14; // `f64` 是 `Sized` let z: bool = true; // `bool` 是 `Sized` let s: String = String::from("hello"); // `String` 是 `Sized`
println!("{}, {}, {}, {}", x, y, z, s);}Sized 和动态大小类型(DST)
Section titled “Sized 和动态大小类型(DST)”某些类型在编译时大小未知,因此它们不实现 Sized。这些类型通常被称为动态大小类型(Dynamically Sized Types,DST)。常见的 DST 包括:
[T]:切片类型,例如[i32]。str:字符串切片类型,例如&str。
由于这些类型的大小在编译时未知,它们不能直接作为函数参数或变量。通常需要将它们放在某种指针类型(例如引用或智能指针)中,以便编译器可以处理它们。
fn main() { let s: &str = "hello"; // `&str` 是动态大小类型 `str` 的引用 let a: &[i32] = &[1, 2, 3]; // `&[i32]` 是动态大小类型 `[i32]` 的引用
println!("{}, {:?}", s, a);}Sized 在泛型中的使用
Section titled “Sized 在泛型中的使用”在泛型编程中,Rust 默认要求泛型类型参数实现 Sized。如果希望泛型参数可以是动态大小类型,需要显式地放宽这一限制。
// 默认情况下,T 必须实现 Sizedfn generic_function<T>(value: T) { // ...}
// 允许 T 是一个动态大小类型fn generic_function_unsized<T: ?Sized>(value: &T) { // ...}在第二个示例中,T: ?Sized 表示 T 可以是一个动态大小类型,并且函数参数 value 是一个对 T 的引用。
自定义类型和 Sized
Section titled “自定义类型和 Sized”当定义一个结构体或枚举时,如果包含动态大小类型的字段,结构体或枚举本身也会变成动态大小类型,需要放在某种指针类型中使用。
struct MyStruct<T: ?Sized> { data: T,}
fn main() { // 错误:无法直接创建动态大小类型的实例 // let s = MyStruct { data: [1, 2, 3] };
// 正确:使用引用或智能指针 let s: &MyStruct<[i32]> = &MyStruct { data: [1, 2, 3] };
println!("{:?}", s.data);}在 Rust 中,Copy 是一个标记 trait,用于表示一个类型的值可以按位复制(bitwise copy)。实现了 Copy trait 的类型在赋值或传递时会进行浅拷贝,而不是移动。这使得该类型的值在赋值和传递时更加方便和高效。
Copy 的定义
Section titled “Copy 的定义”Copy trait 的定义非常简单:
pub trait Copy : Clone { }这个定义表示 Copy 是一个标记 trait,且所有实现了 Copy 的类型也必须实现 Clone。
自动实现 Copy 的类型
Section titled “自动实现 Copy 的类型”以下类型默认实现了 Copy:
- 所有的基本类型,例如
i32、u32、f64、bool、char等。 - 包含所有字段都实现了
Copy的结构体。 - 固定大小的数组,例如
[i32; 3]。
以下是一些实现了 Copy 的类型示例:
fn main() { // 基本类型 let x: i32 = 5; let y = x; // x 被复制到 y
println!("x: {}, y: {}", x, y);
// 固定大小的数组 let arr: [i32; 3] = [1, 2, 3]; let arr2 = arr; // arr 被复制到 arr2
println!("arr: {:?}, arr2: {:?}", arr, arr2);
// 实现了 Copy 的结构体 #[derive(Copy, Clone)] struct Point { x: i32, y: i32, }
let p1 = Point { x: 0, y: 0 }; let p2 = p1; // p1 被复制到 p2
println!("p1: ({}, {}), p2: ({}, {})", p1.x, p1.y, p2.x, p2.y);}手动实现 Copy
Section titled “手动实现 Copy”如果你有一个自定义类型,并且希望它实现 Copy,可以使用 #[derive(Copy, Clone)] 属性来自动生成 Copy 和 Clone 实现。
#[derive(Copy, Clone)]struct MyStruct { a: i32, b: f64,}
fn main() { let s1 = MyStruct { a: 42, b: 3.14 }; let s2 = s1; // s1 被复制到 s2
println!("s1: ({}, {}), s2: ({}, {})", s1.a, s1.b, s2.a, s2.b);}在这个示例中,MyStruct 通过 #[derive(Copy, Clone)] 自动实现了 Copy 和 Clone。
Copy 和 Clone 的区别
Section titled “Copy 和 Clone 的区别”虽然 Copy 和 Clone 都可以用于复制值,但它们有一些重要区别:
Copy:类型的值可以按位复制,赋值时会自动进行复制操作。Copy是一种浅拷贝,适用于简单的值类型。Clone:需要显式调用clone方法进行复制,适用于需要深拷贝的复杂类型。
#[derive(Clone)]struct ComplexType { data: Vec<i32>,}
fn main() { let c1 = ComplexType { data: vec![1, 2, 3] }; let c2 = c1.clone(); // 需要显式调用 clone 方法
println!("c1: {:?}, c2: {:?}", c1.data, c2.data);}在这个示例中,ComplexType 通过 #[derive(Clone)] 实现了 Clone,但由于它包含 Vec(一个未实现 Copy 的类型),所以不能实现 Copy。
何时使用 Copy
Section titled “何时使用 Copy”Copy 适用于小而简单的值类型,例如:
- 基本类型(整数、浮点数、布尔值、字符)。
- 实现了
Copy的类型的组合(例如结构体或数组)。
对于包含堆分配数据或需要复杂管理的类型,应使用 Clone 而不是 Copy。
在 Rust 中,Clone 是一个 trait,用于表示一个类型可以显式地复制自身。与 Copy 不同,Clone 可以用于复杂的类型,允许深拷贝,即在堆上分配的数据也会被复制。
Clone 的定义
Section titled “Clone 的定义”Clone trait 的定义如下:
pub trait Clone { fn clone(&self) -> Self; fn clone_from(&mut self, source: &Self) { ... }}clone方法:用于创建一个类型的副本。clone_from方法:允许在已有的实例上复制数据,通常用于优化。
自动实现 Clone
Section titled “自动实现 Clone”许多标准库类型默认实现了 Clone,包括:
- 所有基本类型,例如
i32、f64、bool等。 - 标准库中的大多数集合类型,例如
String、Vec<T>、HashMap<K, V>等。 - 包含所有字段都实现了
Clone的结构体和枚举。
可以使用 #[derive(Clone)] 来自动为自定义类型实现 Clone。
#[derive(Clone)]struct MyStruct { a: i32, b: String,}
fn main() { let s1 = MyStruct { a: 42, b: String::from("hello"), }; let s2 = s1.clone();
println!("s1: ({}, {}), s2: ({}, {})", s1.a, s1.b, s2.a, s2.b);}在这个示例中,MyStruct 通过 #[derive(Clone)] 自动实现了 Clone,可以调用 clone 方法来创建一个副本。
Clone 和 Copy 的区别
Section titled “Clone 和 Copy 的区别”虽然 Clone 和 Copy 都可以用于复制值,但它们有一些重要区别:
Copy:浅拷贝,赋值时会自动进行复制操作。适用于简单的值类型。Clone:需要显式调用clone方法进行复制,适用于需要深拷贝的复杂类型。
#[derive(Copy, Clone)]struct Point { x: i32, y: i32,}
fn main() { let p1 = Point { x: 0, y: 0 }; let p2 = p1; // 自动复制(Copy)
let p3 = p1.clone(); // 显式复制(Clone)
println!("p1: ({}, {}), p2: ({}, {}), p3: ({}, {})", p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);}在这个示例中,Point 既实现了 Copy 也实现了 Clone,可以自动复制或显式地调用 clone 方法。
自定义 Clone 实现
Section titled “自定义 Clone 实现”在某些情况下,你可能需要自定义 Clone 的实现方式。例如,如果类型包含指针或其他需要手动管理的资源。
struct MyStruct { data: Vec<i32>,}
impl Clone for MyStruct { fn clone(&self) -> Self { MyStruct { data: self.data.clone(), } }}
fn main() { let s1 = MyStruct { data: vec![1, 2, 3] }; let s2 = s1.clone();
println!("s1: {:?}, s2: {:?}", s1.data, s2.data);}在这个示例中,MyStruct 包含一个 Vec<i32>,我们自定义了 clone 方法,以便正确地复制 Vec 的内容。
使用 clone_from
Section titled “使用 clone_from”clone_from 方法允许在已有的实例上复制数据,通常用于减少内存分配,提高性能。
#[derive(Clone)]struct MyStruct { data: Vec<i32>,}
fn main() { let s1 = MyStruct { data: vec![1, 2, 3] }; let mut s2 = MyStruct { data: vec![4, 5, 6] };
s2.clone_from(&s1);
println!("s1: {:?}, s2: {:?}", s1.data, s2.data);}Future
Section titled “Future”在 Rust 中,Future 是异步编程的核心概念之一。它代表一个可能在未来某个时间点完成的值或计算。Future 允许你编写非阻塞代码,这在 I/O 操作和并发编程中非常有用。
什么是 Future?
Section titled “什么是 Future?”Future 是一个 trait,它定义了一个异步计算的接口。Future 有一个核心方法 poll,它尝试推进 Future 到一个新的状态。
以下是 Future trait 的定义(简化版):
use std::task::{Context, Poll};
pub trait Future { type Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>;}poll方法:尝试推进Future,可能返回Poll::Pending(表示计算尚未完成)或Poll::Ready(T)(表示计算已经完成,T是结果)。Output类型:表示Future计算完成后的结果类型。
使用 Future
Section titled “使用 Future”通常你不会直接实现 Future trait,而是使用 async/await 语法,因为它可以自动生成实现了 Future 的状态机。
以下是一个简单的异步函数,它返回一个 Future:
async fn hello() -> String { "Hello, world!".to_string()}
#[tokio::main]async fn main() { let greeting = hello().await; println!("{}", greeting);}在这个例子中,hello 函数返回一个 Future,当你在 main 函数中 await 它时,Rust 会自动处理 Future 的状态转换。
手动实现 Future
Section titled “手动实现 Future”尽管通常使用 async/await 语法,你也可以手动实现 Future。以下是一个简单的示例,展示如何手动实现一个计时器 Future:
use std::pin::Pin;use std::task::{Context, Poll};use std::time::{Duration, Instant};use tokio::time::sleep;
struct Timer { when: Instant,}
impl Timer { fn new(duration: Duration) -> Self { Timer { when: Instant::now() + duration, } }}
impl Future for Timer { type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { if Instant::now() >= self.when { Poll::Ready(()) } else { cx.waker().wake_by_ref(); Poll::Pending } }}
#[tokio::main]async fn main() { let timer = Timer::new(Duration::from_secs(2)); timer.await; println!("Timer completed");}Future 的关键概念
Section titled “Future 的关键概念”- 异步计算:
Future代表一个可能尚未完成的计算。 - 非阻塞:
poll方法应该是非阻塞的,这意味着它应该立即返回,而不是等待计算完成。 - 状态机:使用
async/await语法时,编译器会自动将异步函数转换为状态机,以管理Future的状态。
Stream
Section titled “Stream”在Rust编程语言中,Stream 是异步编程中的一个重要概念,类似于迭代器,但用于异步操作。Stream 是一个持续产生值的异步序列,类似于标准库中的 Iterator 特征,但 Stream 特征是异步的。
以下是一些基础知识和示例,帮助你了解并使用 Stream:
- Stream:可以被认为是一个异步版本的迭代器,允许你按顺序生成一系列值。
- async-std:一个常用的异步标准库,提供了
Stream特征。 - futures:另一个常用库,定义了
Stream特征和许多辅助工具。
Stream 特征
Section titled “Stream 特征”Stream 特征定义如下:
use std::pin::Pin;use std::task::{Context, Poll};
pub trait Stream { type Item;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>>;}poll_next方法:异步地尝试从Stream中获取下一个值。Pin:确保对象的内存位置不会被移动。Context:提供Waker,允许Stream在准备好时通知执行器(executor)。Poll:表示一个操作的状态,可以是Poll::Pending或Poll::Ready(Some(Item))或Poll::Ready(None)。
使用 Stream
Section titled “使用 Stream”要使用 Stream,通常需要配合异步运行时,如 Tokio 或 async-std。
以下是一个简单的示例,演示如何使用 Stream:
使用 async-std 和 futures
Section titled “使用 async-std 和 futures”use async_std::stream::Stream;use futures::stream;use futures::StreamExt;
#[async_std::main]async fn main() { let my_stream = stream::iter(vec![1, 2, 3]);
my_stream.for_each(|x| async move { println!("Got: {}", x); }).await;}自定义 Stream
Section titled “自定义 Stream”你也可以创建自定义的 Stream。以下是一个简单的例子:
use std::pin::Pin;use std::task::{Context, Poll};use futures::stream::Stream;
struct Counter { count: usize, max: usize,}
impl Counter { fn new(max: usize) -> Self { Counter { count: 0, max } }}
impl Stream for Counter { type Item = usize;
fn poll_next(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Option<Self::Item>> { if self.count < self.max { self.count += 1; Poll::Ready(Some(self.count)) } else { Poll::Ready(None) } }}
#[tokio::main]async fn main() { let mut counter = Counter::new(5);
while let Some(value) = counter.next().await { println!("Got: {}", value); }}常用的 Stream 操作符
Section titled “常用的 Stream 操作符”next(): 获取Stream中的下一个值。filter(): 过滤Stream中的元素。map(): 映射Stream中的元素。fold(): 对Stream中的元素进行累积操作。
Iterator
Section titled “Iterator”Iterator 是一个 trait,它定义了一系列方法,用于遍历和处理元素。最基本的方法是 next,它返回迭代器中的下一个元素。
以下是 Iterator trait 的定义:
pub trait Iterator { type Item;
fn next(&mut self) -> Option<Self::Item>;
// 其他方法}type Item:表示迭代器生成的元素类型。fn next(&mut self) -> Option<Self::Item>:返回迭代器的下一个元素,如果迭代器已经结束,则返回None。
你可以通过调用集合类型上的 iter 方法来创建迭代器。例如,Vec 类型有一个 iter 方法,可以创建一个元素的迭代器。
fn main() { let v = vec![1, 2, 3]; let mut iter = v.iter();
assert_eq!(iter.next(), Some(&1)); assert_eq!(iter.next(), Some(&2)); assert_eq!(iter.next(), Some(&3)); assert_eq!(iter.next(), None);}自定义迭代器
Section titled “自定义迭代器”你可以实现自己的迭代器,方法是为一个结构体实现 Iterator trait。
struct Counter { count: u32,}
impl Counter { fn new() -> Counter { Counter { count: 0 } }}
impl Iterator for Counter { type Item = u32;
fn next(&mut self) -> Option<Self::Item> { if self.count < 5 { self.count += 1; Some(self.count) } else { None } }}
fn main() { let mut counter = Counter::new();
while let Some(value) = counter.next() { println!("{}", value); }}在这个示例中,Counter 结构体实现了 Iterator trait,并生成从 1 到 5 的数字。
迭代器适配器
Section titled “迭代器适配器”迭代器适配器是 Iterator trait 上定义的方法,这些方法可以对迭代器进行转换或组合。常见的迭代器适配器包括 map、filter、take、collect 等。
-
map:对每个元素应用一个函数,并返回一个新的迭代器。let v = vec![1, 2, 3];let v2: Vec<_> = v.iter().map(|x| x + 1).collect();assert_eq!(v2, vec![2, 3, 4]); -
filter:过滤掉不符合条件的元素,并返回一个新的迭代器。let v = vec![1, 2, 3, 4, 5];let v2: Vec<_> = v.iter().filter(|&&x| x % 2 == 0).collect();assert_eq!(v2, vec![2, 4]); -
take:只取前n个元素,并返回一个新的迭代器。let v = vec![1, 2, 3, 4, 5];let v2: Vec<_> = v.iter().take(3).collect();assert_eq!(v2, vec![&1, &2, &3]); -
collect:将迭代器转换为集合,例如Vec、HashSet等。let v = vec![1, 2, 3];let v2: Vec<_> = v.iter().collect();assert_eq!(v2, vec![&1, &2, &3]);
消耗适配器是一些会消耗迭代器的方法,通常会遍历整个迭代器并产生一个值。例如:
-
sum:计算所有元素的和。let v = vec![1, 2, 3, 4];let sum: i32 = v.iter().sum();assert_eq!(sum, 10); -
count:计算元素的个数。let v = vec![1, 2, 3, 4];let count = v.iter().count();assert_eq!(count, 4); -
fold:将元素组合为一个值。let v = vec![1, 2, 3, 4];let sum = v.iter().fold(0, |acc, &x| acc + x);assert_eq!(sum, 10);