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