Module wyz::tap

source · []
Expand description

Object Tapping

This crate provides traits for transparently inserting operations into a method chain. All traits take and return the object on which they act by value, and run a provided function on a borrow of the value.

This allows methods that do not chain (such as mutators with &mut self -> () signatures) to be chained.

The traits in this crate provide methods that run some function, Fn(&T) or Fn(&mut T), on a value T without changing the binding status of that value.

Value Tapping

The primary trait of this crate is Tap, which provides two methods: tap and tap_mut. These provide immutable or mutable, respectively, borrows of the tapped value to a user-provided function. The user function must not have a return value.

This permits using inspector-style (Fn(&Self)) or mutator-style (Fn(&mut Self)) functions in a method chain without breaks or reduction of access to the main value.

Tap methods never change the type of the object on which they are called. The mut-suffixed methods are permitted to change the value of their object.

Trait Tapping

Rust does not have subtyping in the object-oriented sense; rather, it uses traits to indicate relationships between types and bring behavior of an interior type to the exterior type. This crate provides taps that use the standard conversion traits in order to assist in running tap methods generically.

Borrowed Tapping

The traits std::borrow::Borrow and std::borrow::BorrowMut allow container types to behave as their contained types in borrowed contexts. The TapBorrow trait provides methods, tap_borrow and tap_borrow_mut, which depend on Borrow and BorrowMut, respectively, to run the user-provided function on the borrowed interior type.

This is useful for inspecting the interior of a Cow or other data structures that abstract away the exact container type but provide uniform access to the underlying data.

Polymorphic Tapping

The traits std::convert::AsRef and std::convert::AsMut allow composed types to be used by reference as their component types. The TapAsRef trait provides methods, tap_ref and tap_ref_mut, which depend on AsRef and AsMut, respectively, to run the user-provided function on the referred component type.

This is useful for working with types like Path, which are commonly used as generic targets such as <P: AsRef<Path>>. All such types P may have .tap_ref called upon them with methods implemented on Path.

Note: Borrow and AsRef are generic traits, which a type can implement many times with different targets. As such, the referent type must be specified in the tapped function. This can be done with a named method, or by marking the type of the closure argument: |x: &Referent| ....

Dereferenced Tapping

The traits std::ops::Deref and std::ops::DerefMut may be used to make owning containers transparently defer to their contained data. This is used by Vec and String, for example, to behave like [T] and str implicitly.

The TapDeref trait provides tap_deref and tap_deref_mut which call Deref or DerefMut, respectively, on the tapped value before running the provided function on the produced Deref::Target value.

Since Deref may only be implemented once, this trait does not require any extra type information in its tap calls.

Conditional Tapping

Additional traits are provided to only invoke the tap when certain conditions are met in the value being tapped.

Boolean Tapping

The [TapBool] trait, with methods [tap_true], [tap_false], and their associated _mut variants, run the provided function only when the value is of the correct variant. This trait is implemented on bool by default, and is left open so that user crates may implement it on their own bool-like types.

Optional Tapping

The TapOption trait, with methods tap_some, tap_some_mut, and tap_none, run the provided function only when the Option is of the matching variant. The tap_some methods pass &T or &mut T to their function; tap_none passes nothing.

Note that tap_some_mut may change the value of the inner object, but it cannot change the Option from Some to None. If this behavior is desired, use tap_mut to modify the Option wrapper directly, rather than tap_some_mut to change the interior value.

Result Tapping

This acts exactly like TapOption, except that the alternate case has a value that may be modified. It thus has methods tap_ok, tap_err, and the associated _mut variants.

Debug Tapping

All methods in the crate have a sibling method with the exact same name and signature, except that the name is suffixed with _dbg. This method runs the normal tap in a debug build, and is removed in release builds.

use wyz::tap::TapOption;

Some(5i32).tap_some_dbg(|n| debug!("{}", n));

This emits a debug trace when the crate is built in debug mode, and does nothing when the crate is built in release mode.

Usage

Import the trait or traits you wish to use, with use wyz::tap::Tap;, and then attach .tap methods on the end of any expression you want to inspect or modify. These methods never change the type or binding status of the object to which they are attached, and can be added or removed without affecting neighboring code.

Examples

This uses tap_mut to modify a vector using methods that cannot be chained, and without converting to an iterator and re-collecting.

use wyz::tap::Tap;

let v = vec![5, 1, 2, 4, 3]
  .tap_mut(|v| v.sort())
  .tap_mut(|v| v.iter_mut().for_each(|e| *e *= 2))
  .tap_mut(|v| v.reverse());
assert_eq!(&v, &[10, 8, 6, 4, 2]);

This uses tap_some to implement a conditional flag.

use wyz::tap::TapOption;

let mut flag = false;

let n = None::<i32>.tap_some(|_| flag = true);
assert!(n.is_none());
assert!(!flag);

let n: Option<i32> = Some(1).tap_some(|_| flag = true);
assert_eq!(n.unwrap(), 1);
assert!(flag);

And this uses tap_err to log errors without suppressing them.

use wyz::tap::TapResult;

let mut err_ct = 0;

{
 let mut action = |e: &&str| {
  err_ct += 1;
  eprintln!("ERROR: {}", e);
 };

 Ok::<_, &str>("success").tap_err(&mut action);
 Err::<(), _>("failure").tap_err(&mut action);
} // I didn't want to write the closure twice

assert_eq!(err_ct, 1);
//  printed "ERROR: failure"

!

Traits

Value Tap

Referential Tap

Borrowing Tap

Dereferencing Tap

Optional Tap

Result Tap