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

Download history 499552/week @ 2023-12-15 289587/week @ 2023-12-22 389918/week @ 2023-12-29 545638/week @ 2024-01-05 545341/week @ 2024-01-12 592848/week @ 2024-01-19 592643/week @ 2024-01-26 596992/week @ 2024-02-02 603223/week @ 2024-02-09 562058/week @ 2024-02-16 619408/week @ 2024-02-23 620307/week @ 2024-03-01 623704/week @ 2024-03-08 649360/week @ 2024-03-15 634495/week @ 2024-03-22 504822/week @ 2024-03-29

每月下载量 2,525,469
3,169crate中使用(直接使用120个)

MIT 许可证

39KB
422 代码行

tap

后缀位置管道行为

Crate Documentation License

Crate Downloads Crate Size

这个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 模块是最简单的:它提供了两个特质,ConvTryConv,它们是 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();

完整功能

TapPipe 特质都提供大量方法,这些方法使用 Rust 语言设施的不同部分来访问类型良好的值。这里不再重复 API 文档,您应查看 文档 中的模块项。

总之,这些特质在收到一个值时提供的方法是:

  • 不进行转换
  • 应用 AsRefAsMut 实现
  • 应用 BorrowBorrowMut 实现
  • 应用 DerefDerefMut 实现

在执行它们的效应参数之前。

此外,每个 Tap 方法 .tap_x 都有一个同族方法 .tap_x_dbg,它执行相同的工作,但在调试构建中;在发布构建中,方法调用被剥离。这允许您在源代码中留下调试 taps,而不影响实际使用中项目的性能。

最后,tap 模块还有 traits TapOptionalTapFallible,分别对 OptionResult 枚举的变体进行 tap 操作,当变体与方法名称不匹配时,什么都不做。当在 None 上调用 TapOptional::tap_some 时没有效果,等等。

没有运行时依赖