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
:
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:
(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:
let x = Something::new()
.foo()
.tap(|thing| log!(thing))
.bar()
.tap(|thing| log!(thing))
.baz()
Here's my naive implementation of Tap
:
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.