# Pre-RFC: \`Tap\` and \`TapMut\` traits

**URL:** <https://internals.rust-lang.org/t/pre-rfc-tap-and-tapmut-traits/14839>\
**Category:** libs\
**Created:** [June 5, 2021, 2:19am UTC](https://internals.rust-lang.org/t/pre-rfc-tap-and-tapmut-traits/14839 "2021-06-05T02:19:13Z")\
**Posts on this page:** 15\
**Page:** 1

<div class="post-metadata">

**Author:** ![woodruffw](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/woodruffw/32/10387_2.png) [@woodruffw](https://internals.rust-lang.org/u/woodruffw)\
**Post date:** [June 5, 2021, 2:19am UTC](https://internals.rust-lang.org/t/pre-rfc-tap-and-tapmut-traits/14839/1 "2021-06-05T02:19:13Z")

</div>

Hi all,

Apologies if this isn't the correct venue for posting this. If so, please just let me know!

I'd like to propose two new, potentially blanket traits for/in the Rust standard library: `Tap` and `TapMut`.

For those familiar with Ruby, the concept behind these traits is identical to Ruby's [`Object#tap`](https://ruby-doc.org/core-2.7.1/Object.html#method-i-tap):

> Yields self to the block, and then returns self. The primary purpose of this method is to “tap into” a method chain, in order to perform operations on intermediate results within the chain.

Here's what that looks like in Ruby:

```ruby
(1..10) .tap {|x| puts "original: #{x}" }
  .to_a .tap {|x| puts "array: #{x}" }
  .select {|x| x.even? } .tap {|x| puts "evens: #{x}" }
  .map {|x| x*x } .tap {|x| puts "squares: #{x}" }

```

Since Rust doesn't have default mutability the way Ruby does, the `Tap` trait's primary purpose would be allowing users to interpose e.g. logging into a long chain of methods, without having to break out into a temporary variable or other (less explicit) binding context. `TapMut` would provide the mutable counterpart, allowing a user to interpose modifications to an object.

By way of example, here's how a `Tap` trait could be used to debug a fluent-style API:

```rust
let x = Something::new()
  .foo()
  .tap(|thing| log!(thing))
  .bar()
  .tap(|thing| log!(thing))
  .baz()

```

Here's my naive implementation of `Tap`:

```rust
pub trait Tap {
    fn tap<F>(&self, f: F) -> &Self
    where
        F: Fn(&Self) -> (),
    {
        f(&self);
        &self
    }
}

```

I suspect `TapMut` would be nearly identical, but with `FnMut` instead.

I appreciate any thoughts on this! This is also my first attempt at writing a (pre) RFC for Rust, so I'd appreciate any pointers on style or presentation.

---

<div class="post-metadata">

**Author:** ![skysch](https://avatars.discourse-cdn.com/v4/letter/s/f05b48/32.png) [@skysch](https://internals.rust-lang.org/u/skysch)\
**Post date:** [June 5, 2021, 2:57am UTC](https://internals.rust-lang.org/t/pre-rfc-tap-and-tapmut-traits/14839/2 "2021-06-05T02:57:05Z")

</div>

Why does this need to be in std, rather than a crate? Does it facilitate any ecosystem-wide interfaces?

I just noticed it [already is a crate](https://crates.io/crates/tap).

---

<div class="post-metadata">

**Author:** ![XAMPPRocky](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/xampprocky/32/6049_2.png) [@XAMPPRocky](https://internals.rust-lang.org/u/XAMPPRocky)\
**Post date:** [June 5, 2021, 4:56am UTC](https://internals.rust-lang.org/t/pre-rfc-tap-and-tapmut-traits/14839/3 "2021-06-05T04:56:25Z")

</div>

I don’t think these need to be traits, I think they would be mostly useful as new additions to `Iterator`. Just speaking for myself I would like to see these added though, I already have seen people (ab)use `map` for this purpose, it would be nice if this was its own operation.

---

<div class="post-metadata">

**Author:** ![jdahlstrom](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/jdahlstrom/32/3351_2.png) [@jdahlstrom](https://internals.rust-lang.org/u/jdahlstrom)\
**Post date:** [June 5, 2021, 5:05am UTC](https://internals.rust-lang.org/t/pre-rfc-tap-and-tapmut-traits/14839/4 "2021-06-05T05:05:46Z")

</div>

For Iterator there’s already `inspect` exactly for this purpose.

---

<div class="post-metadata">

**Author:** ![XAMPPRocky](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/xampprocky/32/6049_2.png) [@XAMPPRocky](https://internals.rust-lang.org/u/XAMPPRocky)\
**Post date:** [June 5, 2021, 5:06am UTC](https://internals.rust-lang.org/t/pre-rfc-tap-and-tapmut-traits/14839/5 "2021-06-05T05:06:53Z")

</div>

TIL! Thanks 🙂

---

<div class="post-metadata">

**Author:** ![bluss](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/bluss/32/2264_2.png) [@bluss](https://internals.rust-lang.org/u/bluss)\
**Post date:** [June 5, 2021, 12:57pm UTC](https://internals.rust-lang.org/t/pre-rfc-tap-and-tapmut-traits/14839/6 "2021-06-05T12:57:20Z")

</div>

This seems useful. (The following is intended as friendly advice). A pre-rfc and rfc would make more sense after you've gathered some real-world experience with this idea implemented into Rust - using the tap crate or any other implementation.

We can also understand skysch's question _Why does this need to be in std, rather than a crate?_ maybe not as a dismissal, but a real question that an RFC would have to answer. 🙂

This interface sort of made me think of dbg!() which is also made to be able to be interposed, and maybe that's a good thing - the community likes these kinds of things.

---

<div class="post-metadata">

**Author:** ![dhm](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/dhm/32/4879_2.png) [@dhm](https://internals.rust-lang.org/u/dhm)\
**Post date:** [June 5, 2021, 6:48pm UTC](https://internals.rust-lang.org/t/pre-rfc-tap-and-tapmut-traits/14839/7 "2021-06-05T18:48:35Z")

</div>

FWIW, the one I find very useful is `.tap_mut()`, actually, to get more fine-grained control over `mut`ability of an item just while constructing it (_à la_ `.also` in Kotlin):

```rust
let ref target_dir =
    PathBuf::from(env::var_os("CARGO_MANIFEST_DIR")?).tap_mut(|p| {
        p.push("target"); // assume a simple case for the example
        if for_wasm() {
            p.push("wasm32-unknown-unknown");
        }
    })
;
let debug_dir;
#[cfg(mistake)] {
    debug_dir = target_dir.push("debug"); // Error, only `&` access
}
#[cfg(not(mistake))] {
    debug_dir = target_dir.join("debug"); // OK
}
let release_dir = target_dir.join("release");
println!("{:?}\n{:?}", debug_dir, release_dir);
other_stuff(target_dir);

```

- On a tangentially related note, I'd love a `PathBuf -> PathBuf` "append a path segment" _pipelineable_ operation: `.join(…).join(…).join(…)` is just so sad.

---

<div class="post-metadata">

**Author:** ![woodruffw](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/woodruffw/32/10387_2.png) [@woodruffw](https://internals.rust-lang.org/u/woodruffw)\
**Post date:** [June 5, 2021, 10:47pm UTC](https://internals.rust-lang.org/t/pre-rfc-tap-and-tapmut-traits/14839/8 "2021-06-05T22:47:35Z")

</div>

Thanks for the thoughtful responses thus far!

To address a few:

@skysch:

> Why does this need to be in std, rather than a crate? Does it facilitate any ecosystem-wide interfaces?

I don't think it _needs_ to be in `std`, but that it's a generally useful feature (particularly when writing and/or debugging fluent APIs, which both `std` and large chunks of the crate ecosystem adhere to. I also think it's a sufficiently generic concept that already has siblings in `std` (`dbg!`, as @bluss mentioned), which makes it a decent candidate for inclusion.

@XAMPPRocky

> I don’t think these need to be traits, I think they would be mostly useful as new additions to `Iterator` .

It's possible that I'm misunderstanding, but I _think_ that `Tap` and `TapMut` would be useful outside of an `Iterator` context -- there are a number of APIs that are fluent-style that would benefit from being `tap`-able that aren't themselves `impl Iterator` and don't make sense to be (like `std::process::Command`). @jdahlstrom brings up `inspect` which is indeed exactly this for `Iterator`, but to use `inspect` for `T where T: !Iterator` you'd either need to do the `Option<T>` trick or some other one. Those feel a little clunky to me, which is why I initially proposed an entirely separate trait. But again, very possible that I'm misunderstanding 🙂

@bluss:

> A pre-rfc and rfc would make more sense after you've gathered some real-world experience with this idea implemented into Rust - using the tap crate or any other implementation.

> We can also understand skysch's question _Why does this need to be in std, rather than a crate?_ maybe not as a dismissal, but a real question that an RFC would have to answer.

Thank you, and thanks for the framing! I'll dedicate some time to developing real-world samples for this idea, and update this thread with them. I've tried to answer @skysch's question haphazardly above, but it certainly merits a less handwave-y justification.

---

<div class="post-metadata">

**Author:** ![CAD97](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cad97/32/3460_2.png) [@CAD97](https://internals.rust-lang.org/u/CAD97)\
**Post date:** [June 5, 2021, 11:35pm UTC](https://internals.rust-lang.org/t/pre-rfc-tap-and-tapmut-traits/14839/9 "2021-06-05T23:35:32Z")

</div>

The interesting part is that there's three kinds of receivers (ignoring arbitrary ones), and thus three styles of fluent APIs in use today.

Specifically, all of `f(self, ...) -> Self`, `f(&self, ...) -> &Self`, and `f(&mut self, ...) -> &mut Self` are used for different purposes.

If the final consumer wants to take ownership (common in builder APIs), then intervening steps have to take self by value to chain to the consumer. Typesafe builders also need to change the type through the building methods. The way self is passed through usually is decided by how the end consumer wants to consume the value.

If tap upgrades to std, I'd want to see either a design that manages to just offer `tap(self, impl FnOnce(&mut Self)) -> Self` rather than also `tap_ref(&self, impl FnOnce(&Self)) -> &Self` and `tap_mut(&mut self, impl FnOnce(&mut Self)) -> &mut Self`, or a strong argument why the former isn't sufficient. Always just using `tap` is better than using `tap_ref` and `tap_mut` for by[-mut]-ref chains.

(Also, I'd just like to see a note as to whether this is expected to mostly supersede `let val = { let val = /*...*/; /*...*/; val };` blocks.)

Then there's the question of whether people would import the trait, or just open code it. Importing the trait from std is definitely lower burden than importing a trait from a crate, but I doubt any blanket extension trait would be added to the prelude, and tap is on the same line as `dbg!` where its utility comes from always being there, rather than something you have to ask for, since just open coding it is not all that expensive.

---

<div class="post-metadata">

**Author:** ![riking](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/riking/32/2818_2.png) [@riking](https://internals.rust-lang.org/u/riking)\
**Post date:** [June 6, 2021, 6:07pm UTC](https://internals.rust-lang.org/t/pre-rfc-tap-and-tapmut-traits/14839/10 "2021-06-06T18:07:09Z")

</div>

> [@woodruffw](#):
>
> but with `FnMut` instead.

> [@CAD97](#):
>
> ... FnOnce ...

I think this should be called out explicitly - @woodruffw, the function should be `FnOnce`, because `tap` or `tap_mut` will only call the function once.

---

<div class="post-metadata">

**Author:** ![scottmcm](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/scottmcm/32/2355_2.png) [@scottmcm](https://internals.rust-lang.org/u/scottmcm)\
**Post date:** [June 6, 2021, 7:09pm UTC](https://internals.rust-lang.org/t/pre-rfc-tap-and-tapmut-traits/14839/11 "2021-06-06T19:09:03Z")

</div>

> [@CAD97](#):
>
> Then there's the question of whether people would import the trait, or just open code it.

Open coding it might get easier in future, too. Not saying we _will_, but if `.match` exists then `.tap(x => foo(x))` is `.match { x => { foo(x); x } }` -- and `tap_move` is even easier.

---

<div class="post-metadata">

**Author:** ![synek317](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/synek317/32/3371_2.png) [@synek317](https://internals.rust-lang.org/u/synek317)\
**Post date:** [June 7, 2021, 12:08pm UTC](https://internals.rust-lang.org/t/pre-rfc-tap-and-tapmut-traits/14839/12 "2021-06-07T12:08:58Z")

</div>

> [@skysch](#):
>
> Why does this need to be in std, rather than a crate?

Indeed, it doesn't. On the other hand, small things, like this, are less likely to be used if they require fetching another dependency.

Btw. I would rather rename `tap` to `inspect` and `tap_mut` to just `tap`.

---

<div class="post-metadata">

**Author:** ![voidc](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/voidc/32/8162_2.png) [@voidc](https://internals.rust-lang.org/u/voidc)\
**Post date:** [June 8, 2021, 7:45am UTC](https://internals.rust-lang.org/t/pre-rfc-tap-and-tapmut-traits/14839/13 "2021-06-08T07:45:00Z")

</div>

I used to write lots of Kotlin code and made heavy use of the _scope functions_ [`also`](https://kotlinlang.org/docs/scope-functions.html#apply) and [`apply`](https://kotlinlang.org/docs/scope-functions.html#apply). The `also` function is equivalent to the proposed `tap` function. The `apply` function returns the result of the given closure:

```rust
    fn apply<F, R>(self, f: F) -> R
        where
            F: FnOnce(Self) -> R;

```

In Rust projects I usually end up implementing similar functions manually. I think both would be useful additions to the standard library, as people usually wouldn't want to add a crate dependency just for those functions.

---

<div class="post-metadata">

**Author:** ![pickfire](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/pickfire/32/3830_2.png) [@pickfire](https://internals.rust-lang.org/u/pickfire)\
**Post date:** [June 16, 2021, 3:21am UTC](https://internals.rust-lang.org/t/pre-rfc-tap-and-tapmut-traits/14839/14 "2021-06-16T03:21:50Z")

</div>

Adding `tap` as doc alias to `inspect`. [Add tap doc alias for Iterator inspect by pickfire · Pull Request #86347 · rust-lang/rust · GitHub](https://github.com/rust-lang/rust/pull/86347)

---

<div class="post-metadata">

**Author:** ![system](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/system/32/14092_2.png) [@system](https://internals.rust-lang.org/u/system)\
**Post date:** [September 14, 2021, 3:22am UTC](https://internals.rust-lang.org/t/pre-rfc-tap-and-tapmut-traits/14839/15 "2021-09-14T03:22:33Z")

</div>

Closed on September 14, 2021, 3:22am UTC

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.
