193 lines
6.6 KiB
Markdown
193 lines
6.6 KiB
Markdown
<div align="center">
|
||
|
||
# `wyz` <!-- omit in toc -->
|
||
|
||
[![Crate][crate_img]][crate]
|
||
[![Documentation][docs_img]][docs]
|
||
[![License][license_img]][license_file]
|
||
|
||
[![Continuous Integration][travis_img]][travis]
|
||
[![Code Coverage][codecov_img]][codecov]
|
||
[![Crate Downloads][downloads_img]][crate]
|
||
[![Crate Size][loc_img]][loc]
|
||
|
||
</div>
|
||
|
||
I have developed a collection of utility and convenience Rust modules that are
|
||
useful to me, and may be useful to you also.
|
||
|
||
This crate is a collection of largely-independent small modules. I do not
|
||
currently offer features to disable modules independently of each other, but
|
||
their compilation cost is small enough to essentially not matter.
|
||
|
||
## Modules <!-- omit in toc -->
|
||
|
||
1. [`conv`](#conv)
|
||
1. [`exit`](#exit)
|
||
1. [`fmt`](#fmt)
|
||
1. [`pipe`](#pipe)
|
||
1. [`tap`](#tap)
|
||
|
||
## `conv`
|
||
|
||
This module provides a single trait, of the same name, with a single generic
|
||
method, also of the same name. This trait is a sibling to `Into`, but rather
|
||
than placing its type parameter in the trait (`Into::<T>::into`), `Conv` places
|
||
it in the method: `Conv::conv::<T>`.
|
||
|
||
By placing the type parameter in the method name, `.conv` can be called in
|
||
suffix position in expressions where the result type cannot be inferred and must
|
||
be explicitly stated.
|
||
|
||
```rust
|
||
use wyz::conv::Conv;
|
||
|
||
let digits = 0xabcd.conv::<String>().len();
|
||
```
|
||
|
||
This is a trivial example, but writing a code context where `.conv` makes sense
|
||
takes a lot more context than a `README` wants.
|
||
|
||
## `exit`
|
||
|
||
This is a macro that calls `std::process::exit`. It can return a status code,
|
||
and also print a message to `stderr`.
|
||
|
||
```rust
|
||
use wyz::exit::exit;
|
||
|
||
exit!();
|
||
exit!(2);
|
||
exit!(3, "This is a {} message", "failure");
|
||
```
|
||
|
||
The default call is `std::process::exit(1)`; a call may provide its own exit
|
||
code and, in addition, a set of arguments to pass directly to `eprintln!`. The
|
||
error message is not guaranteed to be emitted, as `stderr` may be closed at time
|
||
of `exit!`.
|
||
|
||
## `fmt`
|
||
|
||
Rust uses the `Debug` trait for automatic printing events in several parts of
|
||
the standard library. This module provides wrapper types which forward their
|
||
`Debug` implementation to a specified other formatting trait. It also implements
|
||
extension methods on all types that have format trait implementations to wrap
|
||
them in the corresponding shim type.
|
||
|
||
```rust
|
||
use wyz::fmt::FmtForward as _;
|
||
|
||
let val = 6;
|
||
let addr = &val as *const i32;
|
||
println!("{:?}", addr.fmt_pointer());
|
||
```
|
||
|
||
This snippet uses the `Debug` format template, but will print the `Pointer`
|
||
implementation of `*const i32`.
|
||
|
||
This is useful for fitting your values into an error-handling framework that
|
||
only uses `Debug`, such as the `fn main() -> Result` program layout.
|
||
|
||
## `pipe`
|
||
|
||
Rust does not permit universal suffix-position function call syntax. That is,
|
||
you can *always* call a function with `Scope::name(arguments…)`, but only *some*
|
||
functions can be called as `first_arg.name(other_args…)`. Working in “data
|
||
pipelines” – flows where the return value of one function is passed directly as
|
||
the first argument to the next – is common enough in our field that it has a
|
||
name in languages that support it: *method chaining*. A *method* is a function
|
||
that the language considers to be treated specially in regards to only its
|
||
first argument, and permits changing the abstract token series
|
||
`function arg1 args…` into the series `arg1 function args…`.
|
||
|
||
Rust restricts that order transformation to only functions defined in scope for
|
||
some type (either `impl Type` or `impl Trait for Type` blocks) and that take a
|
||
first argument named `self`.
|
||
|
||
Other languages permit calling *any* function, regardless of its definition site
|
||
in source code, in this manner, as long as the first argument is of the correct
|
||
type for the first parameter of the function.
|
||
|
||
In languages like F♯ and Elixir, this uses the call operator `|>` rather than
|
||
the C++ family’s `.` caller. This operator is pronounced `pipe`.
|
||
|
||
Rust does not have a pipe operator. The dot-caller is restricted to only the
|
||
implementation blocks listed above, and this is not likely to change because it
|
||
also performs limited type transformation operations in order to find a name
|
||
that fits.
|
||
|
||
This module provides a `Pipe` trait whose method, `pipe`, passes its `self`
|
||
first argument as the argument to its second-order function:
|
||
|
||
```rust
|
||
use wyz::pipe::Pipe;
|
||
|
||
let final = 5
|
||
.pipe(|a| a + 10)
|
||
.pipe(|a| a * 2);
|
||
|
||
assert_eq!(final, 30);
|
||
```
|
||
|
||
Without language-level syntax support, piping into closures always requires
|
||
restating the argument, and functions cannot curry the argument they receive
|
||
from `pipe` and arguments from the environment in the manner that dot-called
|
||
methods can.
|
||
|
||
```rust
|
||
fn fma(a: i32, b: i32, c: i32) -> i32 {
|
||
(a * b) + c
|
||
}
|
||
5.pipe(|a| fma(a, 2, 3));
|
||
|
||
let fma_2_3 = |a| fma(a, 2, 3);
|
||
5.pipe(fma_2_3);
|
||
```
|
||
|
||
These are the only ways to express `5 |> fma(2, 3)`.
|
||
|
||
Sorry.
|
||
|
||
Bug the language team.
|
||
|
||
## `tap`
|
||
|
||
Tapping is a cousin operation to piping, except that rather than pass the
|
||
receiver by *value* into some function, and return the result of that function,
|
||
it passes a *borrow* of a value into a function, and then returns the original
|
||
value.
|
||
|
||
It is useful for inserting an operation into an expression without changing the
|
||
overall state (type or value) of the expression.
|
||
|
||
```rust
|
||
use wyz::tap::Tap;
|
||
|
||
let result = complex_value()
|
||
.tap(|v| log::info!("First stage: {}", v))
|
||
.transform(other, args)
|
||
.tap(|v| log::info!("Second stage: {}", v));
|
||
```
|
||
|
||
The tap calls have no effect on the expression into which they are placed,
|
||
except to induce side effects somewhere else in the system. Commenting out the
|
||
two `.tap` calls does not change anything about `complex_value()`, `.transform`,
|
||
or `result`; it only removes the log statements.
|
||
|
||
This enables easily inserting or removing inspection points in an expression
|
||
without otherwise altering it.
|
||
|
||
[codecov]: https://codecov.io/gh/myrrlyn/wyz "Code Coverage"
|
||
[codecov_img]: https://img.shields.io/codecov/c/github/myrrlyn/wyz.svg?logo=codecov "Code Coverage Display"
|
||
[crate]: https://crates.io/crates/wyz "Crate Link"
|
||
[crate_img]: https://img.shields.io/crates/v/wyz.svg?logo=rust "Crate Page"
|
||
[docs]: https://docs.rs/wyz "Documentation"
|
||
[docs_img]: https://docs.rs/wyz/badge.svg "Documentation Display"
|
||
[downloads_img]: https://img.shields.io/crates/dv/wyz.svg?logo=rust "Crate Downloads"
|
||
[license_file]: https://github.com/myrrlyn/wyz/blob/master/LICENSE.txt "License File"
|
||
[license_img]: https://img.shields.io/crates/l/wyz.svg "License Display"
|
||
[loc]: https://github.com/myrrlyn/wyz "Repository"
|
||
[loc_img]: https://tokei.rs/b1/github/myrrlyn/wyz?category=code "Repository Size"
|
||
[travis]: https://travis-ci.org/myrrlyn/wyz "Travis CI"
|
||
[travis_img]: https://img.shields.io/travis/myrrlyn/wyz.svg?logo=travis "Travis CI Display"
|