长久以来,我们习惯了传统老牌编程语言的思维惯式,很难跳出if/else/for/while这一抽象层次,虽然高层上也可以有架构和设计模式,但是具体到每个函数或方法之中,满眼if/else/for/while!那么如何快速地弄清楚函数或方法的意图,第一是需要一个见名知意的函数或方法名,二者是清晰并且及时更新的注释,最后就是通读函数或方法的代码;初生牛犊的程序员也许会说,这不算什么呀!读代码和写代码本来就是我们的本分呀!我理解能力很强的呀。
从业多年并且参与过大型长期项目或产品开发维护的程序员也许会有更深刻的体会!函数不断地被插入新代码,所谓的见名知意也许早就名存实亡了, 函数的注释陈旧且混乱,有时候会误导你, 最后一堆if/else/for/while纠缠在一起,犹如地雷,稍微动几行代码后,很可能全体就瘫痪了!某些架构关键点就混在其中。
接触了解Rust之后,我初步感受到了其强劲清晰的抽象表达能力!当然这会涉及到许多Rust语言的语法项,在此我只讨论Option&Result&Iterator这三个核心要素,若你是花些时间去读一读Rust开源项目, 很容易发现这三个要素几乎形影不离地出目前每一个项目代码中!他们负责穿针引线胶水粘合,协助程序员以更高的抽象水平来表达自己的设计意图!列如常常出现的方法:unwrap(解包),map(映射为), and_then(接下来), filter(过滤掉), next(下一个),collect(收集起来)等等,以接近我们口语的方式来表达编码意图!抽象表达水平更高,更加容易理解和维护扩展,当然前提是你必须接受并熟悉Rust风格,而非偏见排斥;若是没有他们三者,那么Rust函数式编程将大失色彩。
下面我归纳几个Option&Result&Iterator的常用方法,掌握他们就好比是掌握英语口语中的常用词,可以协助我们开口说话。
1. Option
pub enum Option<T> {
None,
Some(T),
}
(1)and 似而非是与操作
pub fn and<U>(self, optb: Option<U>) -> Option<U>
【例子】
//针对以下所有代码:当x是None时,则返回None, 当x是Some时,则返回y!!!
//----------------
let x = Some(2);
let y: Option<&str> = None;
assert_eq!(x.and(y), None);
let x: Option<u32> = None;
let y = Some("foo");
assert_eq!(x.and(y), None);
let x = Some(2);
let y = Some("foo");
assert_eq!(x.and(y), Some("foo"));
let x: Option<u32> = None;
let y: Option<&str> = None;
assert_eq!(x.and(y), None);
(2) and_then (接下来)
pub fn and_then<U, F>(self, f: F) -> Option<U>
where
F: FnOnce(T) -> Option<U>,
【例子】
//当为None时,则返回None,当为Some时,则调用以下函数或闭包,
//注意:将Some中包裹的值取出来作为实参去调用函数或闭包,
//根据函数或闭包运算结果,返回None或Some(结果值)
//---------------------------------------------
fn sq_then_to_string(x: u32) -> Option<String> {
x.checked_mul(x).map(|sq| sq.to_string())
}
assert_eq!(Some(2).and_then(sq_then_to_string), Some(4.to_string()));
assert_eq!(Some(1_000_000).and_then(sq_then_to_string), None); // overflowed!
assert_eq!(None.and_then(sq_then_to_string), None);
(3) filter(过滤掉)
pub fn filter<P>(self, predicate: P) -> Option<T>
where
P: FnOnce(&T) -> bool,
【例子】
//当为None时,则返回None, 当为Some时,则调用以下函数或闭包,
//当函数返回true, 则filter返回Some(当前值),
//否则当函数返回false, 则filter返回None.
fn is_even(n: &i32) -> bool {
n % 2 == 0
}
assert_eq!(None.filter(is_even), None);
assert_eq!(Some(3).filter(is_even), None);
assert_eq!(Some(4).filter(is_even), Some(4));
(4) flatten(去掉一层)
pub const fn flatten(self) -> Option<T>
【例子】
//压平嵌套结构:Option<Option<T>> 为Option<T> ,
//注意调用一次flatten值只压平一层嵌套,而非所有嵌套。
let x: Option<Option<u32>> = Some(Some(6));
assert_eq!(Some(6), x.flatten());
let x: Option<Option<u32>> = Some(None);
assert_eq!(None, x.flatten());
//当以None调用时,则返回None.
let x: Option<Option<u32>> = None;
assert_eq!(None, x.flatten());
//注意:调用一次flatten值压平一层嵌套,如下例子:
let x: Option<Option<Option<u32>>> = Some(Some(Some(6)));
assert_eq!(Some(Some(6)), x.flatten());
assert_eq!(Some(6), x.flatten().flatten());
(5) map(映射为)
pub fn map<U, F>(self, f: F) -> Option<U>
where
F: FnOnce(T) -> U,
【例子】
//当为None时,则返回None,当为Some则调用函数或闭包,
//Some包裹函数运算结果值。
let maybe_some_string = Some(String::from("Hello, World!"));
// `Option::map` takes self *by value*, consuming `maybe_some_string`
let maybe_some_len = maybe_some_string.map(|s| s.len());
assert_eq!(maybe_some_len, Some(13));
let x: Option<&str> = None;
assert_eq!(x.map(|s| s.len()), None);
(6)or (或者)
pub fn or(self, optb: Option<T>) -> Option<T>
【例子】
//当x为Some时,则返回x;
//当x为None时,则返回y;
let x = Some(2);
let y = None;
assert_eq!(x.or(y), Some(2));
let x = None;
let y = Some(100);
assert_eq!(x.or(y), Some(100));
let x = Some(2);
let y = Some(100);
assert_eq!(x.or(y), Some(2));
let x: Option<u32> = None;
let y = None;
assert_eq!(x.or(y), None);
(7) ok_or (将Option<T>转化成Result<T,E>)
pub fn ok_or<E>(self, err: E) -> Result<T, E>
【例子】
let x = Some("foo");
assert_eq!(x.ok_or(0), Ok("foo"));
//当为None时,则用Err包裹ok_or方法实参返回。
let x: Option<&str> = None;
assert_eq!(x.ok_or(0), Err(0));
(8) take (取出留白)
pub const fn take(&mut self) -> Option<T>
【例子】
//x包裹值被取出,然后留白(None)
let mut x = Some(2);
let y = x.take();
assert_eq!(x, None);
assert_eq!(y, Some(2));
let mut x: Option<u32> = None;
let y = x.take();
assert_eq!(x, None);
assert_eq!(y, None);
(9) transpose (内外翻转)
pub const fn transpose(self) -> Result<Option<T>, E>
【例子】
//将Option<Result<T,E>>转化为Result<Option<T>>
//当为None时,则返回Ok(None)
//当为Some(Ok(_))时,则返回OK(Some(_))
//当为Some(Err(_))时,则返回Err(_)
#[derive(Debug, Eq, PartialEq)]
struct SomeErr;
let x: Result<Option<i32>, SomeErr> = Ok(Some(5));
let y: Option<Result<i32, SomeErr>> = Some(Ok(5));
assert_eq!(x, y.transpose());
(10) unwrap(解包,产品代码不提议用)
pub const fn unwrap(self) -> T
【例子】
let x = Some("air");
assert_eq!(x.unwrap(), "air");
//当为None时,调用unwrap,则触发panic, 切记!
let x: Option<&str> = None;
assert_eq!(x.unwrap(), "air"); // fails
(11)as_ref
pub const fn as_ref(&self) -> Option<&T>
将 &Option<T> 转换为 Option<&T>。
let text: Option<String> = Some("Hello, world!".to_string());
// First, cast `Option<String>` to `Option<&String>` with `as_ref`,
// then consume *that* with `map`, leaving `text` on the stack.
let text_length: Option<usize> = text.as_ref().map(|s| s.len());
println!("still can print text: {text:?}");
(12)as_mut
pub const fn as_mut(&mut self) -> Option<&mut T>
将 &mut Option<T> 转换为 Option<&mut T>。
let mut x = Some(2);
match x.as_mut() {
Some(v) => *v = 42,
None => {},
}
assert_eq!(x, Some(42));
(13)as_slice
pub const fn as_slice(&self) -> &[T]
如果包含值存在,则返回该值的切片;如果为 None,则返回空切片。这在需要统一处理 Option 或切片的迭代器时可能很有用。
【例子】
assert_eq!(
[Some(1234).as_slice(), None.as_slice()],
[&[1234][..], &[][..]],
);
fn main(){
assert_eq!(
[Some(1234).as_slice(), None.as_slice()],
[&[1234][..], &[][..]],
);
//a是一个包含一个元素(1234)的切片,切片len为1.
let a = Some(1234).as_slice();
//输出:
//[1234], 1234; []
//注意None对应一个空切片.
println!("{:?}, {}; {:?}", a, a[0],None::<u8>.as_slice());
}
fn main(){
assert_eq!(
[Some(1234).as_slice(), None.as_slice()],
[&[1234][..], &[][..]],
);
//实验环境:rust playground stable 2024.
//a是一个包含一个元素(1234)的切片,切片len为1.
//输出:
//[1234], 1234; []
//注意None对应一个空切片.
let a = Some(1234).as_slice();
println!("{:?}, {}; {:?}", a, a[0],None::<u8>.as_slice());
println!("-------------------");
//输出:
//Some(1234), Some(1234)
//None, None
for i in [Some(1234_u16), None] {
assert_eq!(i.as_ref(), i.as_slice().first());
println!("{:?}, {:?}", i.as_ref(), i.as_slice().first());
}
}
(14)zip
pub fn zip<U>(self, other: Option<U>) -> Option<(T, U)>
将自身与另一个 Option 进行压缩(配对)。
如果 self 是 Some(s) 且 other 是 Some(o),则此方法返回 Some((s, o))。否则,返回 None。
【例子】
let x = Some(1);
let y = Some("hi");
let z = None::<u8>;
assert_eq!(x.zip(y), Some((1, "hi")));
assert_eq!(x.zip(z), None);
注意:上面Option的每一个方法的receiver的不同形式,如:self, &self , &mut self ; 这三种形式具有不同的含义,我们必须保持警惕!列如定义一个Option类型的变量x, 通过x.method调用时,self表明方法会消耗掉x,换言之,x的所有权会被转移进方法中,那么变量x就作废了,后面作用域中的代码就不可以再访问它了,否则编译失败报错!而&self和&mut self只是分别表明只读借用和可变借用变量x掌握的值,不会获取变量x的所有权,确切地说是变量x对于值的所有权,变量x依旧有效,后面作用域中的代码依旧可以访问它;也可以说此方法不会消耗掉变量x。这个总结对于下面的Result和Iterator同样成立!必须遵守!
2. Result
pub enum Result<T, E> {
Ok(T),
Err(E),
}
(1) and
pub fn and<U>(self, res: Result<U, E>) -> Result<U, E>
如果结果是 Ok,则返回 res;否则返回 self 的 Err 值。
传递给 and 的参数会被立即求值;如果您传递的是函数调用的结果,提议使用 and_then,它是延迟求值的。
【例子】
let x: Result<u32, &str> = Ok(2);
let y: Result<&str, &str> = Err("late error");
assert_eq!(x.and(y), Err("late error"));
let x: Result<u32, &str> = Err("early error");
let y: Result<&str, &str> = Ok("foo");
assert_eq!(x.and(y), Err("early error"));
let x: Result<u32, &str> = Err("not a 2");
let y: Result<&str, &str> = Err("late error");
assert_eq!(x.and(y), Err("not a 2"));
let x: Result<u32, &str> = Ok(2);
let y: Result<&str, &str> = Ok("different result type");
assert_eq!(x.and(y), Ok("different result type"));
(2) and_then
pub fn and_then<U, F>(self, op: F) -> Result<U, E>
where
F: FnOnce(T) -> Result<U, E>,
如果结果是 `Ok`,则调用 `op`;否则返回 `self` 的 `Err` 值。
此函数可用于基于 `Result` 值进行流程控制。
【例子】
fn sq_then_to_string(x: u32) -> Result<String, &'static str> {
x.checked_mul(x).map(|sq| sq.to_string()).ok_or("overflowed")
}
assert_eq!(Ok(2).and_then(sq_then_to_string), Ok(4.to_string()));
assert_eq!(Ok(1_000_000).and_then(sq_then_to_string), Err("overflowed"));
assert_eq!(Err("not a number").and_then(sq_then_to_string), Err("not a number"));
(3) map
pub fn map<U, F>(self, op: F) -> Result<U, E>
where
F: FnOnce(T) -> U,
通过将一个函数应用于包含的 `Ok` 值(同时保持 `Err` 值不变),将 `Result<T, E>` 映射为 `Result<U, E>`。此函数可用于组合两个函数的结果。
【例子】
let line = "1
2
3
4
";
for num in line.lines() {
match num.parse::<i32>().map(|i| i * 2) {
Ok(n) => println!("{n}"),
Err(..) => {}
}
}
注意:and_then和map所调用闭包或函数声明的不同,前者返回结果为Result<U,E>包裹的结果值;而后者返回结果就是裸值,切记没有Result包裹;换言之,map所调用的闭包不允许失败,必须返回值,而非Err.
(4) ok
pub fn ok(self) -> Option<T>
将 Result<T, E> 转换为 Option<T>。
将自身转换为 Option<T>,消耗自身,错误转化为None.
【例子】
let x: Result<u32, &str> = Ok(2);
assert_eq!(x.ok(), Some(2));
let x: Result<u32, &str> = Err("Nothing here");
assert_eq!(x.ok(), None);
(5) or
pub fn or<F>(self, res: Result<T, F>) -> Result<T, F>
如果结果是 Err,则返回 res;否则返回 self 的 Ok 值。传递给 or 的参数会被立即求值;如果你要传递的是一个函数调用的结果,提议使用 or_else,它是惰性求值的。
【例子】
let x: Result<u32, &str> = Ok(2);
let y: Result<u32, &str> = Err("late error");
assert_eq!(x.or(y), Ok(2));
let x: Result<u32, &str> = Err("early error");
let y: Result<u32, &str> = Ok(2);
assert_eq!(x.or(y), Ok(2));
let x: Result<u32, &str> = Err("not a 2");
let y: Result<u32, &str> = Err("late error");
assert_eq!(x.or(y), Err("late error"));
let x: Result<u32, &str> = Ok(2);
let y: Result<u32, &str> = Ok(100);
assert_eq!(x.or(y), Ok(2));
(6) transpose
pub const fn transpose(self) -> Option<Result<T, E>>
将一个 Option 的 Result 转置为 Result 的 Option。
具体映射规则如下:
Ok(None) 将被映射为 None
Ok(Some(_)) 和 Err(_) 将分别被映射为 Some(Ok(_)) 和 Some(Err(_))
【例子】
#[derive(Debug, Eq, PartialEq)]
struct SomeErr;
let x: Result<Option<i32>, SomeErr> = Ok(Some(5));
let y: Option<Result<i32, SomeErr>> = Some(Ok(5));
assert_eq!(x.transpose(), y);
(7) unwrap
pub fn unwrap(self) -> T
where
E: Debug,
返回包含的 `Ok` 值,并消耗(移动)掉 `self`。
由于此函数可能会引发 panic,一般不提议使用。Panic 用于表明不可恢复的错误,甚至可能导致整个程序终止。
更推荐的做法是使用 `?`(尝试)操作符、模式匹配来显式处理 `Err` 情况,或者调用 `unwrap_or`、`unwrap_or_else` 或 `unwrap_or_default` 方法。
如果值是 `Err`,则会发生 panic,panic 信息由 `Err` 的值提供。
【例子】
let x: Result<u32, &str> = Ok(2);
assert_eq!(x.unwrap(), 2);
//---------------------------
//切记: 会触发panic.
let x: Result<u32, &str> = Err("emergency failure");
x.unwrap(); // panics with `emergency failure`
(8) flatten
pub const fn flatten(self) -> Result<T, E>
将 Result<Result<T, E>, E> 转换为 Result<T, E>
【例子】
#![feature(result_flattening)]
let x: Result<Result<&'static str, u32>, u32> = Ok(Ok("hello"));
assert_eq!(Ok("hello"), x.flatten());
let x: Result<Result<&'static str, u32>, u32> = Ok(Err(6));
assert_eq!(Err(6), x.flatten());
let x: Result<Result<&'static str, u32>, u32> = Err(6);
assert_eq!(Err(6), x.flatten());
注意:flatten调用一次只能去掉一层,若是多层包裹,则需调用flatten多次。
#![feature(result_flattening)]
let x: Result<Result<Result<&'static str, u32>, u32>, u32> = Ok(Ok(Ok("hello")));
assert_eq!(Ok(Ok("hello")), x.flatten());
assert_eq!(Ok("hello"), x.flatten().flatten());
(9) as_ref
pub const fn as_ref(&self) -> Result<&T, &E>
将 `&Result<T, E>` 转换为 `Result<&T, &E>`。
生成一个新的 `Result`,其中包含对原始数据的引用,同时保持原始数据不变。
let x: Result<u32, &str> = Ok(2);
assert_eq!(x.as_ref(), Ok(&2));
let x: Result<u32, &str> = Err("Error");
assert_eq!(x.as_ref(), Err(&"Error"));
(10) as_mut
pub const fn as_mut(&mut self) -> Result<&mut T, &mut E>
将 &mut Result<T, E> 转换为 Result<&mut T, &mut E>。
【例子】
fn mutate(r: &mut Result<i32, i32>) {
match r.as_mut() {
Ok(v) => *v = 42,
Err(e) => *e = 0,
}
}
let mut x: Result<i32, i32> = Ok(2);
mutate(&mut x);
assert_eq!(x.unwrap(), 42);
let mut x: Result<i32, i32> = Err(13);
mutate(&mut x);
assert_eq!(x.unwrap_err(), 0);
3. Iterator
pub trait Iterator {
type Item;
...
}
(1)next
fn next(&mut self) -> Option<Self::Item>
推进迭代器并返回下一个值,当迭代完成时返回None.
【例子】
let a = [1, 2, 3];
let mut iter = a.into_iter();
// A call to next() returns the next value...
assert_eq!(Some(1), iter.next());
assert_eq!(Some(2), iter.next());
assert_eq!(Some(3), iter.next());
// ... and then None once it's over.
assert_eq!(None, iter.next());
// More calls may or may not return `None`. Here, they always will.
assert_eq!(None, iter.next());
assert_eq!(None, iter.next());
(2)all
fn all<F>(&mut self, f: F) -> bool
where
Self: Sized,
F: FnMut(Self::Item) -> bool,
测试迭代器中的每个元素是否都满足某个谓词条件。
`all()` 方法接收一个返回布尔值(true 或 false)的闭包。它会将该闭包应用到迭代器的每一个元素上,如果所有元素调用该闭包后都返回 true,那么 `all()` 也返回 true;如果其中任何一个元素返回 false,则 `all()` 返回 false。
`all()` 是短路求值的;换句话说,一旦它发现某个元素返回 false,就会立即停止处理,由于无论后续元素如何,最终结果都必定为 false。
对于一个空迭代器,`all()` 返回 true。
【例子】
let a = [1, 2, 3];
let mut iter = a.into_iter();
assert!(!iter.all(|x| x != 2));
// we can still use `iter`, as there are more elements.
assert_eq!(iter.next(), Some(3));
(3)any
fn any<F>(&mut self, f: F) -> bool
where
Self: Sized,
F: FnMut(Self::Item) -> bool,
测试迭代器中的任意元素是否满足某个条件。
`any()` 方法接收一个返回布尔值(true 或 false)的闭包。它将该闭包应用到迭代器的每个元素上,如果其中任意一个元素使闭包返回 true,则 `any()` 也返回 true;如果所有元素都使闭包返回 false,则 `any()` 返回 false。
`any()` 具有短路特性;换句话说,一旦发现某个元素使得闭包返回 true,它就会停止后续处理,由于无论剩下的元素如何,最终结果都必定是 true。
对于空迭代器,`any()` 返回 false。
【例子】
let a = [1, 2, 3];
let mut iter = a.into_iter();
assert!(iter.any(|x| x != 2));
// we can still use `iter`, as there are more elements.
assert_eq!(iter.next(), Some(2));
(4)chain
fn chain<U>(self, other: U) -> Chain<Self, <U as IntoIterator>::IntoIter> ⓘ
where
Self: Sized,
U: IntoIterator<Item = Self::Item>,
接受两个迭代器,并创建一个新的迭代器,按顺序依次遍历这两个迭代器中的元素。
chain() 会返回一个新的迭代器,该迭代器第一遍历第一个迭代器中的值,然后再遍历第二个迭代器中的值。
换句话说,它将两个迭代器像链条一样连接在一起。
once 一般用于将单个值适配进其他类型的迭代序列中。
【例子】
let s1 = "abc".chars();
let s2 = "def".chars();
let mut iter = s1.chain(s2);
assert_eq!(iter.next(), Some('a'));
assert_eq!(iter.next(), Some('b'));
assert_eq!(iter.next(), Some('c'));
assert_eq!(iter.next(), Some('d'));
assert_eq!(iter.next(), Some('e'));
assert_eq!(iter.next(), Some('f'));
assert_eq!(iter.next(), None);
let a1 = [1, 2, 3];
let a2 = [4, 5, 6];
let mut iter = a1.into_iter().chain(a2);
assert_eq!(iter.next(), Some(1));
assert_eq!(iter.next(), Some(2));
assert_eq!(iter.next(), Some(3));
assert_eq!(iter.next(), Some(4));
assert_eq!(iter.next(), Some(5));
assert_eq!(iter.next(), Some(6));
assert_eq!(iter.next(), None);
//If you work with Windows API, you may wish to convert
//OsStr to
//Vec<u16>:
#[cfg(windows)]
fn os_str_to_utf16(s: &std::ffi::OsStr) -> Vec<u16> {
use std::os::windows::ffi::OsStrExt;
s.encode_wide().chain(std::iter::once(0)).collect()
}
(5)enumerate
fn enumerate(self) -> Enumerate<Self> ⓘ
where
Self: Sized,
创建一个迭代器,该迭代器在返回下一个值的同时提供当前的迭代计数。
返回的迭代器会生成一系列元组 (i, val),其中 i 是当前的迭代索引,val 是迭代器返回的值。
enumerate() 将其计数保存为 usize 类型。如果您想使用不同大小的整数进行计数,zip 函数提供了类似的功能。
溢出行为
该方法不会对溢出进行保护,因此如果枚举的元素超过 usize::MAX,可能会产生错误结果或引发 panic。如果启用了溢出检查,则必定会发生 panic。
恐慌(Panic)
如果即将返回的索引会导致 usize 溢出,返回的迭代器可能会发生 panic。
【例子】
let a = ['a', 'b', 'c'];
let mut iter = a.into_iter().enumerate();
assert_eq!(iter.next(), Some((0, 'a')));
assert_eq!(iter.next(), Some((1, 'b')));
assert_eq!(iter.next(), Some((2, 'c')));
assert_eq!(iter.next(), None);
(6) filter
fn filter<P>(self, predicate: P) -> Filter<Self, P> ⓘ
where
Self: Sized,
P: FnMut(&Self::Item) -> bool,
创建一个迭代器,该迭代器使用闭包来判断是否应生成(包含)某个元素。
对于给定的元素,闭包必须返回 true 或 false。返回的迭代器将只生成(包含)那些闭包返回 true 的元素。
【例子】
let a = [0i32, 1, 2];
let mut iter = a.into_iter().filter(|x| x.is_positive());
assert_eq!(iter.next(), Some(1));
assert_eq!(iter.next(), Some(2));
assert_eq!(iter.next(), None);
由于传递给 `filter()` 的闭包接收一个引用,而许多迭代器本身也是对元素进行引用的迭代,这就可能导致一种可能令人困惑的情况:闭包的类型实际上是一个**双引用**(即对引用的引用)。
let s = &[0, 1, 2];
//模式解构:**
let mut iter = s.iter().filter(|x| **x > 1); // needs two *s!
assert_eq!(iter.next(), Some(&2));
assert_eq!(iter.next(), None);
更常见的做法是对参数进行模式解构:&*,以剥离其中一个(属性/元素)。
let s = &[0, 1, 2];
let mut iter = s.iter().filter(|&x| *x > 1); // both & and *
assert_eq!(iter.next(), Some(&2));
assert_eq!(iter.next(), None);
更彻底的模式解构:&&
let s = &[0, 1, 2];
let mut iter = s.iter().filter(|&&x| x > 1); // two &s
assert_eq!(iter.next(), Some(&2));
assert_eq!(iter.next(), None);
(7)filter_map
fn filter_map<B, F>(self, f: F) -> FilterMap<Self, F> ⓘ
where
Self: Sized,
F: FnMut(Self::Item) -> Option<B>,
创建一个同时具备过滤和映射功能的迭代器。
该返回的迭代器仅会生成那些由所提供闭包返回 Some(value) 的值。
filter_map 可用于使过滤和映射的链式调用更加简洁。下面的示例展示了如何将 map().filter().map() 简化为对 filter_map 的单次调用。
【例子】
let a = ["1", "two", "NaN", "four", "5"];
let mut iter = a.iter().filter_map(|s| s.parse().ok());
assert_eq!(iter.next(), Some(1));
assert_eq!(iter.next(), Some(5));
assert_eq!(iter.next(), None);
以下是一样的示例,但使用了 filter 和 map:
let a = ["1", "two", "NaN", "four", "5"];
let mut iter = a.iter().map(|s| s.parse()).filter(|s| s.is_ok()).map(|s| s.unwrap());
assert_eq!(iter.next(), Some(1));
assert_eq!(iter.next(), Some(5));
assert_eq!(iter.next(), None);
(8)flatten
fn flatten(self) -> Flatten<Self> ⓘ
where
Self: Sized,
Self::Item: IntoIterator,
创建一个迭代器,用于展平嵌套结构。
当您拥有一个迭代器的迭代器(即迭代器中包含其他迭代器),或者一个可转换为迭代器的元素的迭代器,并且您希望去除一层间接引用时,这个功能超级有用。
【例子】
let data = vec![vec![1, 2, 3, 4], vec![5, 6]];
let flattened: Vec<_> = data.into_iter().flatten().collect();
assert_eq!(flattened, [1, 2, 3, 4, 5, 6]);
let words = ["alpha", "beta", "gamma"];
// chars() returns an iterator
let merged: String = words.iter()
.map(|s| s.chars())
.flatten()
.collect();
assert_eq!(merged, "alphabetagamma");
扁平化操作适用于任何实现了 IntoIterator trait 的类型,包括 Option 和 Result。
let options = vec![Some(123), Some(321), None, Some(231)];
let flattened_options: Vec<_> = options.into_iter().flatten().collect();
assert_eq!(flattened_options, [123, 321, 231]);
let results = vec![Ok(123), Ok(321), Err(456), Ok(231)];
let flattened_results: Vec<_> = results.into_iter().flatten().collect();
assert_eq!(flattened_results, [123, 321, 231]);
扁平化操作每次仅移除一层嵌套结构。
let d3 = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]];
let d2: Vec<_> = d3.into_iter().flatten().collect();
assert_eq!(d2, [[1, 2], [3, 4], [5, 6], [7, 8]]);
let d1: Vec<_> = d3.into_iter().flatten().flatten().collect();
assert_eq!(d1, [1, 2, 3, 4, 5, 6, 7, 8]);
(9)flat_map
fn flat_map<U, F>(self, f: F) -> FlatMap<Self, U, F> ⓘ
where
Self: Sized,
U: IntoIterator,
F: FnMut(Self::Item) -> U,
创建一个类似于 `map` 的迭代器,但会自动展平嵌套结构。
`map` 适配器超级有用,但它仅在闭包参数产生值时有效。如果闭包返回的是一个迭代器,那么就会多出一层间接结构。而 `flat_map()` 会自动移除这一层额外的间接结构。
你可以将 `flat_map(f)` 理解为:先进行映射(即 `map(f)`),然后展平(即 `.flatten()`),二者在语义上是等价的。
另一种理解 `flat_map()` 的方式是:`map` 的闭包为每个输入元素返回一个项,而 `flat_map()` 的闭包为每个输入元素返回一个迭代器。
【例子】
let words = ["alpha", "beta", "gamma"];
// chars() returns an iterator
let merged: String = words.iter()
.flat_map(|s| s.chars())
.collect();
assert_eq!(merged, "alphabetagamma");
(10)map
fn map<B, F>(self, f: F) -> Map<Self, F> ⓘ
where
Self: Sized,
F: FnMut(Self::Item) -> B,
接受一个闭包,并创建一个迭代器,该迭代器会对每个元素调用该闭包。
`map()` 方法通过其参数(实现了 `FnMut` trait 的某个东西)将一个迭代器转换为另一个迭代器。它生成一个新的迭代器,该迭代器会对原始迭代器的每个元素调用这个闭包。
如果你擅长从类型的角度思考,可以这样理解 `map()`:如果你有一个返回某种类型 A 的元素的迭代器,而你想要一个返回另一种类型 B 的元素的迭代器,那么你可以使用 `map()`,并传入一个接受类型 A 并返回类型 B 的闭包。
从概念上来说,`map()` 类似于 for 循环。不过,由于 `map()` 是惰性的(lazy),它最适合在你已经与其他迭代器一起工作时使用。如果你是为了产生副作用而进行某种循环操作,一般认为使用 for 循环比使用 map() 更符合惯用法。
【例子】
let a = [1, 2, 3];
let mut iter = a.iter().map(|x| 2 * x);
assert_eq!(iter.next(), Some(2));
assert_eq!(iter.next(), Some(4));
assert_eq!(iter.next(), Some(6));
assert_eq!(iter.next(), None);
如果你要执行某种副作用操作,提议优先使用 for 而非 map()。
// don't do this:
(0..5).map(|x| println!("{x}"));
// 它甚至不会执行,由于它是惰性的。Rust 会对此发出警告。
// Instead, use a for-loop:
for x in 0..5 {
println!("{x}");
}
(11)nth
fn nth(&mut self, n: usize) -> Option<Self::Item>
返回迭代器的第n个元素。
与大多数索引操作类似,计数从零开始,因此nth(0)返回第一个值,nth(1)返回第二个值,依此类推。
如果n大于或等于迭代器的长度,nth()将返回None。
【例子】
let a = [1, 2, 3];
assert_eq!(a.into_iter().nth(1), Some(2));
多次调用 nth() 不会重置迭代器。
let a = [1, 2, 3];
let mut iter = a.into_iter();
assert_eq!(iter.nth(1), Some(2));
assert_eq!(iter.nth(1), None);
如果元素数量少于 n + 1 个,则返回 None。
let a = [1, 2, 3];
assert_eq!(a.into_iter().nth(10), None);
需要注意的是,所有在返回元素之前的元素以及该返回元素本身都会从迭代器中被消耗掉。这意味着前面的元素将被丢弃。
let a = [1, 2, 3];
let mut it = a.into_iter();
assert_eq!(it.nth(1), Some(2));
assert_eq!(it.nth(0), Some(3));
并且多次在同一迭代器上调用nth(0)会返回不同的元素。
let a = [1, 2, 3];
let mut it = a.into_iter();
assert_eq!(it.nth(0), Some(1));
assert_eq!(it.nth(0), Some(2));
assert_eq!(it.nth(0), Some(3));
(12)rev
fn rev(self) -> Rev<Self> ⓘ
where Self: Sized + DoubleEndedIterator,
反转迭代器的方向。
一般,迭代器是从左向右迭代的。使用 rev() 后,迭代器将改为从右向左迭代。
只有当迭代器有明确的终点时,这种操作才可行,因此 rev() 仅适用于实现了 DoubleEndedIterator(双向迭代器)的迭代器。
【例子】
let a = [1, 2, 3];
let mut iter = a.into_iter().rev();
assert_eq!(iter.next(), Some(3));
assert_eq!(iter.next(), Some(2));
assert_eq!(iter.next(), Some(1));
assert_eq!(iter.next(), None);
(13)reduce
fn reduce<F>(self, f: F) -> Option<Self::Item>
where
Self: Sized,
F: FnMut(Self::Item, Self::Item) -> Self::Item,
通过反复应用一个归约操作,将多个元素缩减为单个元素。
如果迭代器为空,则返回 None;否则,返回归约操作的结果。
该归约函数是一个闭包,接受两个参数:一个“累加器”和一个元素。对于至少包含一个元素的迭代器来说,这等同于以该迭代器的第一个元素作为初始累加器值,然后使用 fold() 方法将后续每个元素逐步归约到累加器中。
【例子】
let reduced: i32 = (1..10).reduce(|acc, e| acc + e).unwrap_or(0);
assert_eq!(reduced, 45);
// Which is equivalent to doing it with `fold`:
let folded: i32 = (1..10).fold(0, |acc, e| acc + e);
assert_eq!(reduced, folded);
(14)fold
fn fold<B, F>(self, init: B, f: F) -> B
where
Self: Sized,
F: FnMut(B, Self::Item) -> B,
通过应用一个操作将每个元素折叠进累加器,并返回最终结果。
`fold()` 接受两个参数:一个初始值,以及一个闭包。该闭包有两个参数:一个是“累加器”(accumulator),另一个是当前元素。闭包返回的是下一次迭代时累加器应该持有的值。
初始值就是第一次调用闭包时累加器的值。
在对迭代器的每一个元素应用这个闭包之后,`fold()` 返回最终的累加器值。
这种操作有时也被称为“reduce”(归约)或“inject”(注入)。
当你有一个集合,并希望从中生成一个单一的值时,折叠操作就超级有用。
注意:`fold()` 以及类似的会遍历整个迭代器的方法,对于无限迭代器可能不会终止,即使对于某些在有限时间内可以确定结果的特性也是如此。
注意:如果累加器类型和元素类型一样,可以使用 `reduce()` 方法,它将第一个元素作为初始值。
注意:`fold()` 是以左结合(left-associative)的方式组合元素的。对于像 `+` 这样的结合性操作符来说,元素组合的顺序并不重大;但对于像 `-` 这样的非结合性操作符,顺序会影响最终结果。如果想要一个右结合版本的折叠操作,可以参考 `
DoubleEndedIterator::rfold()`。
对实现者的说明:
其他一些(正向)方法默认是基于这个方法实现的,因此如果你的实现能比默认的 for 循环实现更高效,提议显式地实现这个方法。
特别是,尽量让这个方法调用组成该迭代器的内部部分的 `fold()` 方法。
【例子】
let a = [1, 2, 3];
// the sum of all of the elements of the array
let sum = a.iter().fold(0, |acc, x| acc + x);
assert_eq!(sum, 6);
此示例展示了 `fold()` 的**左结合性**:它从初始值开始构建一个字符串,并依次处理集合中从前到后的每个元素。
let numbers = [1, 2, 3, 4, 5];
let zero = "0".to_string();
let result = numbers.iter().fold(zero, |acc, &x| {
format!("({acc} + {x})")
});
assert_eq!(result, "(((((0 + 1) + 2) + 3) + 4) + 5)");
对于那些不常使用迭代器的人来说,一般会使用一个带有事物列表的 for 循环来构建结果。这些都可以改写为 fold() 的形式:
let numbers = [1, 2, 3, 4, 5];
let mut result = 0;
// for loop:
for i in &numbers {
result = result + i;
}
// fold:
let result2 = numbers.iter().fold(0, |acc, &x| acc + x);
// they're the same
assert_eq!(result, result2);
(15)collect
fn collect<B>(self) -> B
where
B: FromIterator<Self::Item>,
Self: Sized,
将一个迭代器转换为集合。
`collect()` 方法可以接受任何可迭代对象,并将其转换为相应的集合类型。这是标准库中功能较为强劲的方法之一,在多种上下文中都有广泛应用。
`collect()` 最常见的用法模式是将一种集合转换为另一种集合。你先获取一个集合,对其调用 `iter()` 方法,进行一系列转换操作,最后通过 `collect()` 将其转换为新的集合。
`collect()` 还可以创建一些非典型集合类型的实例。例如,可以从一系列字符构建一个 `String`,也可以将一个包含 `Result<T, E>` 类型元素的迭代器收集为一个 `Result<Collection<T>, E>`。更多示例请参见下文。
由于 `collect()` 的通用性很强,它可能会导致类型推断方面的问题。因此,`collect()` 是少数几个你会见到被称为turbofish语法(即 `::< >`)的场景之一。这种语法有助于类型推断算法明确你具体想要收集到哪种集合类型中。
【例子】
let a = [1, 2, 3];
let doubled: Vec<i32> = a.iter()
.map(|x| x * 2)
.collect();
assert_eq!(vec![2, 4, 6], doubled);
请注意,我们需要在左侧加上 : Vec<i32>。这是由于我们也可以将结果收集到其他类型中,例如 VecDeque<T>。
use std::collections::VecDeque;
let a = [1, 2, 3];
let doubled: VecDeque<i32> = a.iter().map(|x| x * 2).collect();
assert_eq!(2, doubled[0]);
assert_eq!(4, doubled[1]);
assert_eq!(6, doubled[2]);
使用“turbofish”语法替代对 doubled 进行类型注解。
let a = [1, 2, 3];
let doubled = a.iter().map(|x| x * 2).collect::<Vec<i32>>();
assert_eq!(vec![2, 4, 6], doubled);
由于 collect() 只关心你要收集到什么类型中,所以你依旧可以使用部分类型提示 _,配合 turbofish 语法使用:
let a = [1, 2, 3];
let doubled = a.iter().map(|x| x * 2).collect::<Vec<_>>();
assert_eq!(vec![2, 4, 6], doubled);
Using collect() to make a String:
let chars = ['g', 'd', 'k', 'k', 'n'];
let hello: String = chars.into_iter()
.map(|x| x as u8)
.map(|x| (x + 1) as char)
.collect();
assert_eq!("hello", hello);
如果你有一个包含多个 Result<T, E> 的列表,可以使用 collect() 方法来检查其中是否有任何一个失败了。
let results = [Ok(1), Err("nope"), Ok(3), Err("bad")];
let result: Result<Vec<_>, &str> = results.into_iter().collect();
//虽然上面results集合列表中有Ok也有Err,但是
//只要遇到Err, 则collect认为失败,
//直接返回遇到的第一个Err作为自己的错误。
// gives us the first error
assert_eq!(Err("nope"), result);
let results = [Ok(1), Ok(3)];
let result: Result<Vec<_>, &str> = results.into_iter().collect();
// gives us the list of answers
assert_eq!(Ok(vec![1, 3]), result);
let opts = [Some(1), None, Some(3), None];
let result: Vec<_> = opts.into_iter().collect();
println!("{:?}", result);
//输出:[Some(1), None, Some(3), None]
//输出和opts一样, 这说明collect遇到None没有失败,
//直接作为普通元素收集到新集合中。
(16)zip
fn zip<U>(self, other: U) -> Zip<Self, <U as IntoIterator>::IntoIter> ⓘ
where
Self: Sized,
U: IntoIterator,
将两个迭代器“压缩”成一个由成对元素组成的单一迭代器。
zip() 函数返回一个新的迭代器,该迭代器会依次遍历另外两个迭代器,并返回一个元组,其中第一个元素来自第一个迭代器,第二个元素来自第二个迭代器。
换句话说,它将两个迭代器“压缩”在一起,合并成一个迭代器。
如果其中任意一个迭代器返回 None,则压缩后的迭代器的 next() 方法也将返回 None。如果压缩后的迭代器没有更多元素可返回,那么后续每次尝试推进它时,将第一尝试最多推进第一个迭代器一次,如果它依旧有元素返回,则再尝试最多推进第二个迭代器一次。
要“解压”两个迭代器压缩后的结果,请参见 unzip。
【例子】
let s1 = "abc".chars();
let s2 = "def".chars();
let mut iter = s1.zip(s2);
assert_eq!(iter.next(), Some(('a', 'd')));
assert_eq!(iter.next(), Some(('b', 'e')));
assert_eq!(iter.next(), Some(('c', 'f')));
assert_eq!(iter.next(), None);
由于 zip() 函数的参数使用了 IntoIterator 特征,我们可以传入任何可以转换为迭代器(Iterator)的类型,而不仅仅是迭代器本身。例如,数组([T])实现了 IntoIterator,因此可以直接传递给 zip() 函数。
let a1 = [1, 2, 3];
let a2 = [4, 5, 6];
let mut iter = a1.into_iter().zip(a2);
assert_eq!(iter.next(), Some((1, 4)));
assert_eq!(iter.next(), Some((2, 5)));
assert_eq!(iter.next(), Some((3, 6)));
assert_eq!(iter.next(), None);
`zip()` 常用于将一个无限迭代器与一个有限迭代器进行配对。之所以可行,是由于有限迭代器最终会返回 `None`(或耗尽),从而结束配对过程。将 `(0..)`(一个从 0 开始的无限整数序列)与某个迭代器进行 `zip()` 操作,看起来很像使用 `enumerate()`。
let enumerate: Vec<_> = "foo".chars().enumerate().collect();
let zipper: Vec<_> = (0..).zip("foo".chars()).collect();
assert_eq!((0, 'f'), enumerate[0]);
assert_eq!((0, 'f'), zipper[0]);
assert_eq!((1, 'o'), enumerate[1]);
assert_eq!((1, 'o'), zipper[1]);
assert_eq!((2, 'o'), enumerate[2]);
assert_eq!((2, 'o'), zipper[2]);
如果两个迭代器的语法大致相当,使用 zip 可能会更具可读性。
use std::iter::zip; //an fn, to zip two iterator.
let a = [1, 2, 3];
let b = [2, 3, 4];
let mut zipped = zip(
a.into_iter().map(|x| x * 2).skip(1),
b.into_iter().map(|x| x * 2).skip(1),
);
assert_eq!(zipped.next(), Some((4, 6)));
assert_eq!(zipped.next(), Some((6, 8)));
assert_eq!(zipped.next(), None);
上面的代码相较于下面的代码更加简洁清晰:
let mut zipped = a
.into_iter()
.map(|x| x * 2)
.skip(1)
.zip(b.into_iter().map(|x| x * 2).skip(1));
(17)cycle
fn cycle(self) -> Cycle<Self> ⓘ
where
Self: Sized + Clone,
无限循环迭代器。
该迭代器不会在结束时停止(如遇到None),而是会从头开始重新迭代。每次迭代完成后,它都会再次从开头重新开始。如此循环往复,永不停止。需要注意的是,如果原始迭代器为空,那么生成的迭代器也将为空。
【例子】
let a = [1, 2, 3];
let mut iter = a.into_iter().cycle();
loop {
assert_eq!(iter.next(), Some(1));
assert_eq!(iter.next(), Some(2));
assert_eq!(iter.next(), Some(3));
}
(18)find
fn find<P>(&mut self, predicate: P) -> Option<Self::Item>
where
Self: Sized,
P: FnMut(&Self::Item) -> bool,
查找迭代器中满足谓词条件的元素。
find() 方法接收一个返回布尔值的闭包。该闭包会依次作用于迭代器的每个元素,只要其中任意一个元素使闭包返回 true,find() 就会返回 Some(元素)。若所有元素都使闭包返回 false,则返回 None。
find() 具有短路特性——也就是说,一旦闭包返回 true,它就会立即停止后续处理。
由于 find() 接收的是引用参数,而许多迭代器本身也是遍历引用,这可能导致参数变成双重引用的情况(例如示例中的 &&x),可能造成理解上的困惑。
如果需要获取元素的索引位置,请使用 position() 方法。
【例子】
let a = [1, 2, 3];
assert_eq!(a.into_iter().find(|&x| x == 2), Some(2));
assert_eq!(a.into_iter().find(|&x| x == 5), None);
Note that iter.find(f) is equivalent to iter.filter(f).next().
let a = [1, 2, 3];
let mut iter = a.into_iter();
assert_eq!(iter.find(|&x| x == 2), Some(2));
// we can still use `iter`, as there are more elements.
assert_eq!(iter.next(), Some(3));
(19)find_map
fn find_map<B, F>(&mut self, f: F) -> Option<B>
where
Self: Sized,
F: FnMut(Self::Item) -> Option<B>,
将函数应用于迭代器的元素,并返回第一个非 None 的结果。
iter.find_map(f) 等价于 iter.filter_map(f).next()。
【例子】
let a = ["lol", "NaN", "2", "5"];
let first_number = a.iter().find_map(|s| s.parse().ok());
assert_eq!(first_number, Some(2));
(20)for_each
fn for_each<F>(self, f: F)
where
Self: Sized,
F: FnMut(Self::Item),
对迭代器的每个元素调用一个闭包。
这相当于在迭代器上使用 for 循环,不过在闭包中无法使用 break 和 continue。一般来说,使用 for 循环更符合惯用法,但在处理较长迭代器链末端的元素时,for_each 可能更具可读性。在某些情况下,for_each 也可能比循环更快,由于它会在像 Chain 这样的适配器上使用内部迭代。
【例子】
use std::sync::mpsc::channel;
let (tx, rx) = channel();
(0..5).map(|x| x * 2 + 1)
.for_each(move |x| tx.send(x).unwrap());
let v: Vec<_> = rx.iter().collect();
assert_eq!(v, vec![1, 3, 5, 7, 9]);
对于这样一个小例子,使用 for 循环可能更简洁,但对于较长的迭代器,使用 for_each 可能更有利于保持函数式编程风格。
【例子】
(0..5).flat_map(|x| x * 100 .. x * 110)
.enumerate()
.filter(|&(i, x)| (i + x) % 3 == 0)
.for_each(|(i, x)| println!("{i}:{x}"));
(21)scan
fn scan<St, B, F>(self, initial_state: St, f: F) -> Scan<Self, St, F> ⓘ
where
Self: Sized,
F: FnMut(&mut St, Self::Item) -> Option<B>,
这是一个迭代器适配器,与 `fold` 类似,它持有内部状态,但与 `fold` 不同的是,它会产生一个新的迭代器。
`scan()` 接受两个参数:一个初始值,用于初始化内部状态;以及一个闭包,该闭包带有两个参数,第一个是对内部状态的可变引用,第二个是迭代器中的当前元素。该闭包可以修改内部状态,从而在多次迭代之间共享状态。
在每次迭代时,该闭包将被应用到迭代器的每个元素上,而闭包的返回值(一个 `Option`)会通过 `next` 方法返回。因此,闭包可以通过返回 `Some(value)` 来生成一个值,或者返回 `None` 来结束迭代。
【例子】
let a = [1, 2, 3, 4];
let mut iter = a.into_iter().scan(1, |state, x| {
// each iteration, we'll multiply the state by the element ...
*state = *state * x;
// ... and terminate if the state exceeds 6
if *state > 6 {
return None;
}
Some(*state)
});
assert_eq!(iter.next(), Some(1));
assert_eq!(iter.next(), Some(2));
assert_eq!(iter.next(), Some(6));
assert_eq!(iter.next(), None);
4. 参考资料:
https://doc.rust-lang.org/std/option/enum.Option.html
https://doc.rust-lang.org/std/result/enum.Result.html
https://doc.rust-lang.org/std/iter/trait.Iterator.html
5. 后记: 个人学习笔记,仓促行文,加之水平有限,难免谬误,还请海涵谅解,不吝指教,好脑瓜不如烂笔头,粗鄙之文权当备忘,抛砖引玉。
【代码仓库】
https://gitee.com/yujinliang-pure/rust-learn
https://github.com/yujinliang/rust_learn




![侠盗猎车手5终极整合包v1.67|含所有DLC+中文+免安装+修改器工具[Win10/11兼容] 网盘下载 - 宋马](https://pic.songma.com/blogimg/20250602/c434f1675d754cd5bc5adea56eb8a2fd.jpg)
















- 最新
- 最热
只看作者