8 个版本 (2 个稳定版)
使用旧 Rust 2015
1.0.1 | 2021年2月13日 |
---|---|
1.0.0 | 2020年9月14日 |
0.4.0 | 2019年4月19日 |
0.3.1 | 2019年1月11日 |
0.1.0 | 2017年6月24日 |
在 Rust 模式 中排名 24
每月下载量 2,525,469
在 3,169 个crate中使用(直接使用120个)
39KB
422 代码行
这个crate为所有类型提供扩展方法,允许透明、临时、检查/修改(提取)、转换(管道)或类型转换。这些方法使您能够轻松地在表达式插入调试或修改点,而无需更改代码的其他部分。
示例用法
提取
您可以在方法链表达式内部进行提取,用于日志记录而无需重新绑定。例如,您可能编写一个复杂的表达式而没有任何中间调试步骤,而只是稍后决定想要它们。通常,这种转换看起来像这样
extern crate reqwest;
extern crate tracing;
// old
let body = reqwest::blocking::get("https://example.com")?
.text()?;
tracing::debug!("Response contents: {}", body);
// new, with debugging
let resp = reqwest::blocking::get("https://example.com")?;
tracing::debug!("Response status: {}", resp.status());
let body = resp.text()?;
tracing::debug!("Response contents: {}", body);
但是,使用提取,您可以直接将日志语句连接到整体表达式,而无需进行任何其他更改
extern crate reqwest;
extern crate tracing;
let body = reqwest::blocking::get("https://example.com")?
// The only change is the insertion of this line
.tap(|resp| tracing::debug!("Response status: {}", resp.status()))
.text()?;
tracing::debug!("Response contents: {}", body);
可变提取
某些API编写为需要可变借用,而不是值到值的转换,这可能导致在不可变环境中创建可变性的临时重新绑定。例如,将数据收集到向量中,排序向量,然后将其冻结,可能看起来像这样
let mut collection = stream().collect::<Vec<_>>();
collection.sort();
// potential error site: inserting other mutations here
let collection = collection; // now immutable
但是,使用可变提取,您可以避免重复绑定 并 防止由于存在可变绑定而引起的未来错误
let collection = stream.collect::<Vec<_>>()
.tap_mut(|v| v.sort());
.tap_mut()
和相关方法为其参数提供可变借用,并允许最终绑定点选择自己的可变性级别,而不暴露中间权限。
管道
除了透明检查或修改点之外,您还可能希望使用后缀调用进行后续操作。例如,标准库提供了将类似于 Path
的对象转换为它们文件系统内容 Vec<u8>
的免费函数 fs::read
。通常,免费函数需要使用
use std::fs;
let mut path = get_base_path();
path.push("logs");
path.push(&format!("{}.log", today()));
let contents = fs::read(path)?;
whereas 使用“tapping”(用于路径修改)和“piping”(用于 fs::read
)可以表示如下
use std::fs;
let contents = get_base_path()
.tap_mut(|p| p.push("logs"))
.tap_mut(|p| p.push(&format!("{}.log", today())))
.pipe(fs::read)?;
作为一个更清晰的例子,考虑应用多个免费函数而不使用 let
-绑定所需的语法,如下所示
let val = last(
third(
second(
first(original_value),
another_arg,
)
),
another_arg,
);
这要求按交替的、内外顺序读取表达式,以理解完整的评估顺序。使用后缀调用,即使是免费函数也可以写成点免费风格,保持清晰的时序和语法顺序
let val = original_value
.pipe(first)
.pipe(|v| second(v, another_arg))
.pipe(third)
.pipe(|v| last(v, another_arg));
由于管道是一个普通方法,而不是语法转换,因此在管道中使用具有多个参数的函数时,仍然需要您编写函数调用表达式。
转换
conv
模块是最简单的:它提供了两个特质,Conv
和 TryConv
,它们是 Into<T>
和 TryInto<T>
的同族特质。它们的方法 Conv::conv::<T>
和 TryConv::try_conv::<T>
调用相应的特质实现,并允许您在表达式的非终端方法调用中使用 .into()
/.try_into()
。
let bytes = "hello".into().into_bytes();
无法编译,因为 Rust 无法确定 "hello".into()
的类型。您不必将表达式重写为使用中间 let
绑定,可以写成
let bytes = "hello".conv::<String>().into_bytes();
完整功能
Tap
和 Pipe
特质都提供大量方法,这些方法使用 Rust 语言设施的不同部分来访问类型良好的值。这里不再重复 API 文档,您应查看 文档 中的模块项。
总之,这些特质在收到一个值时提供的方法是:
- 不进行转换
- 应用
AsRef
或AsMut
实现 - 应用
Borrow
或BorrowMut
实现 - 应用
Deref
或DerefMut
实现
在执行它们的效应参数之前。
此外,每个 Tap
方法 .tap_x
都有一个同族方法 .tap_x_dbg
,它执行相同的工作,但在调试构建中;在发布构建中,方法调用被剥离。这允许您在源代码中留下调试 taps,而不影响实际使用中项目的性能。
最后,tap
模块还有 traits TapOptional
和 TapFallible
,分别对 Option
和 Result
枚举的变体进行 tap 操作,当变体与方法名称不匹配时,什么都不做。当在 None
上调用 TapOptional::tap_some
时没有效果,等等。