1<div class="title-block" style="text-align: center;" align="center">
2
3# `tap` <!-- omit in toc -->
4
5## Suffix-Position Pipeline Behavior <!-- omit in toc -->
6
7[![Crate][crate_img]][crate]
8[![Documentation][docs_img]][docs]
9[![License][license_img]][license_file]
10
11[![Crate Downloads][downloads_img]][crate]
12[![Crate Size][loc_img]][loc]
13
14</div>
15
16This crate provides extension methods on all types that allow transparent,
17temporary, inspection/mutation (tapping), transformation (piping), or type
18conversion. These methods make it convenient for you to insert debugging or
19modification points into an expression without requiring you to change any other
20portions of your code.
21
22## Example Use
23
24### Tapping
25
26You can tap inside a method-chain expression for logging without requiring a
27rebind. For instance, you may write a complex expression without any
28intermediate debugging steps, and only later decide that you want them.
29Ordinarily, this transform would look like this:
30
31```rust
32extern crate reqwest;
33extern crate tracing;
34
35// old
36let body = reqwest::blocking::get("https://example.com")?
37  .text()?;
38tracing::debug!("Response contents: {}", body);
39
40// new, with debugging
41let resp = reqwest::blocking::get("https://example.com")?;
42tracing::debug!("Response status: {}", resp.status());
43let body = resp.text()?;
44tracing::debug!("Response contents: {}", body);
45```
46
47while with tapping, you can plug the logging statement directly into the overall
48expression, without making any other changes:
49
50```rust
51extern crate reqwest;
52extern crate tracing;
53
54let body = reqwest::blocking::get("https://example.com")?
55  // The only change is the insertion of this line
56  .tap(|resp| tracing::debug!("Response status: {}", resp.status()))
57  .text()?;
58tracing::debug!("Response contents: {}", body);
59```
60
61### Mutable Tapping
62
63Some APIs are written to require mutable borrows, rather than value-to-value
64transformations, which can require temporary rebinding in order to create
65mutability in an otherwise-immutable context. For example, collecting data into
66a vector, sorting the vector, and then freezing it, might look like this:
67
68```rust
69let mut collection = stream().collect::<Vec<_>>();
70collection.sort();
71// potential error site: inserting other mutations here
72let collection = collection; // now immutable
73```
74
75But with a mutable tap, you can avoid the duplicate binding *and* guard against
76future errors due to the presence of a mutable binding:
77
78```rust
79let collection = stream.collect::<Vec<_>>()
80  .tap_mut(|v| v.sort());
81```
82
83The `.tap_mut()` and related methods provide a mutable borrow to their argument,
84and allow the final binding site to choose their own level of mutability without
85exposing the intermediate permission.
86
87### Piping
88
89In addition to transparent inspection or modification points, you may also wish
90to use suffix calls for subsequent operations. For example, the standard library
91offers the free function `fs::read` to convert `Path`-like objects into
92`Vec<u8>` of their filesystem contents. Ordinarily, free functions require use
93as:
94
95```rust
96use std::fs;
97
98let mut path = get_base_path();
99path.push("logs");
100path.push(&format!("{}.log", today()));
101let contents = fs::read(path)?;
102```
103
104whereäs use of tapping (for path modification) and piping (for `fs::read`) could
105be expressed like this:
106
107```rust
108use std::fs;
109
110let contents = get_base_path()
111  .tap_mut(|p| p.push("logs"))
112  .tap_mut(|p| p.push(&format!("{}.log", today())))
113  .pipe(fs::read)?;
114```
115
116As a clearer example, consider the syntax required to apply multiple free
117funtions without `let`-bindings looks like this:
118
119```rust
120let val = last(
121  third(
122    second(
123      first(original_value),
124      another_arg,
125    )
126  ),
127  another_arg,
128);
129```
130
131which requires reading the expression in alternating, inside-out, order, to
132understand the full sequence of evaluation. With suffix calls, even free
133functions can be written in a point-free style that maintains a clear temporal
134and syntactic order:
135
136```rust
137let val = original_value
138  .pipe(first)
139  .pipe(|v| second(v, another_arg))
140  .pipe(third)
141  .pipe(|v| last(v, another_arg));
142```
143
144As piping is an ordinary method, not a syntax transformation, it still requires
145that you write function-call expressions when using a function with multiple
146arguments in the pipeline.
147
148### Conversion
149
150The `conv` module is the simplest: it provides two traits, `Conv` and `TryConv`,
151which are sibling traits to `Into<T>` and `TryInto<T>`. Their methods,
152`Conv::conv::<T>` and `TryConv::try_conv::<T>`, call the corresponding
153trait implementation, and allow you to use `.into()`/`.try_into()` in
154non-terminal method calls of an expression.
155
156```rust
157let bytes = "hello".into().into_bytes();
158```
159
160does not compile, because Rust cannot decide the type of `"hello".into()`.
161Instead of rewriting the expression to use an intermediate `let` binding, you
162can write it as
163
164```rust
165let bytes = "hello".conv::<String>().into_bytes();
166```
167
168## Full Functionality
169
170The `Tap` and `Pipe` traits both provide a large number of methods, which use
171different parts of the Rust language’s facilities for well-typed value access.
172Rather than repeat the API documentation here, you should view the module items
173in the [documentation][docs].
174
175As a summary, these traits provide methods that, upon receipt of a value,
176
177- apply no transformation
178- apply an `AsRef` or `AsMut` implementation
179- apply a `Borrow` or `BorrowMut` implementation
180- apply the `Deref` or `DerefMut` implementation
181
182before executing their effect argument.
183
184In addition, each `Tap` method `.tap_x` has a sibling method `.tap_x_dbg` that
185performs the same work, but only in debug builds; in release builds, the method
186call is stripped. This allows you to leave debugging taps in your source code,
187without affecting your project’s performance in true usage.
188
189Lastly, the `tap` module also has traits `TapOptional` and `TapFallible` which
190run taps on the variants of `Option` and `Result` enums, respectively, and do
191nothing when the variant does not match the method name. `TapOptional::tap_some`
192has no effect when called on a `None`, etc.
193
194<!-- Badges -->
195[crate]: https://crates.io/crates/tap "Crate Link"
196[crate_img]: https://img.shields.io/crates/v/tap.svg?logo=rust "Crate Page"
197[docs]: https://docs.rs/tap "Documentation"
198[docs_img]: https://docs.rs/tap/badge.svg "Documentation Display"
199[downloads_img]: https://img.shields.io/crates/dv/tap.svg?logo=rust "Crate Downloads"
200[license_file]: https://github.com/myrrlyn/tap/blob/master/LICENSE.txt "License File"
201[license_img]: https://img.shields.io/crates/l/tap.svg "License Display"
202[loc]: https://github.com/myrrlyn/tap "Repository"
203[loc_img]: https://tokei.rs/b1/github/myrrlyn/tap?category=code "Repository Size"
204