2 releases
0.1.1 | Feb 15, 2022 |
---|---|
0.1.0 | Oct 29, 2021 |
#1707 in Rust patterns
8KB
haz
A thin abstraction over polymorphic environments.
Motivation
Consider a scenario where we want to pass some data to a couple of functions.
Perhaps we've got a type Config
representing our app's configuration which
wraps a bunch of data (e.g. Host
, Port
, Verbosity
, Restriction
, and maybe a bunch of extra fields):
struct Config {
host: Host,
port: Port,
verbosity: Verbosity,
restriction: Restriction,
// ...
}
We might want to pass this data around to a couple of functions which would then use the relevant fields to take some action:
fn do_something_with_host_port_verbosity(...) {
// ...
}
// ...
We could pass a reference to the whole Config
to each function:
fn do_something_with_host_port_verbosity(cfg: &Config) {
//...
}
// ...
Perhaps not every function needs to know about every single field and we might want to avoid such an unnecessary coupling.
One way to go about it is to explicitly pass each field to each function that requires them:
fn do_something_with_host_port_verbosity(host: &Host, port: &Port, restriction: &Restriction) {
// ...
}
// ...
However at usage site it might get tedious since we need to pass each field individually:
let cfg = read_config();
do_something_with_host_port_verbosity(&cfg.host, &cfg.port, &cfg.verbosity);
I can haz data?
The idea behind haz is to help in achieving both:
- Don't unnecessarily pass data to functions that don't require access to it
- Don't require passing each field individually
Everything floats around the thin trait Has<Component>
:
trait Has<Component> {
fn access(&self) -> &Component;
}
By implementing Has<Component>
for some type Container
, we're stating that Container
can yield read-only access to Component
.
Equipped with this trait and assuming we have implemented Has<Host>
, Has<Port>
, Has<Verbosity>
, etc (maybe with impl_has_for_named_component
)
for Config
, we may leverage it as:
fn do_something_with_host_port_verbosity<C>(cfg: &C)
where
C: Has<Host> + Has<Port> + Has<Verbosity> {
//...
}
// ...
We've managed to explicitly state exactly what we as do_something_with_host_port_verbosity
require and hence will get access to that but nothing else.
At usage site, it would look like:
let cfg = read_config();
do_something_with_host_port_verbosity(&cfg);
We're simply passing a reference to Config
and not each field individually.
In summary, we've managed to achieve both of ours goals:
- Don't unnecessarily pass data to functions that don't require access to it
- Don't require passing each field individually