#conversion #convert #newtype

macro no-std shrinkwraprs

Auto-derive for Rust conversion traits – make working with newtypes a breeze

9 releases

0.3.0 Dec 12, 2019
0.2.3 Nov 19, 2019
0.2.1 Jan 25, 2019
0.2.0 Feb 11, 2018
0.0.2 Feb 4, 2018

#39 in Rust patterns

Download history 1604/week @ 2019-10-13 2411/week @ 2019-10-20 2219/week @ 2019-10-27 3232/week @ 2019-11-03 2871/week @ 2019-11-10 3261/week @ 2019-11-17 2643/week @ 2019-11-24 3421/week @ 2019-12-01 2729/week @ 2019-12-08 1712/week @ 2019-12-15 950/week @ 2019-12-22 844/week @ 2019-12-29 1196/week @ 2020-01-05 1866/week @ 2020-01-12 1803/week @ 2020-01-19

10,768 downloads per month
Used in 44 crates (19 directly)

BSD-3-Clause

31KB
552 lines

shrinkwraprs pipeline status

latest version api documentation license

Making wrapper types allows us to give more compile-time guarantees about our code being correct:

// Now we can't mix up widths and heights; the compiler will yell at us!
struct Width(u64);
struct Height(u64);

But... they're kind of a pain to work with. If you ever need to get at that wrapped u64, you need to constantly pattern-match back and forth to wrap and unwrap the values.

shrinkwraprs aims to alleviate this pain by allowing you to derive implementations of various conversion traits by deriving Shrinkwrap.

Functionality implemented

Currently, using #[derive(Shrinkwrap)] will derive the following traits for all structs:

  • AsRef<InnerType>
  • Borrow<InnerType>
  • Deref<Target=InnerType>

Additionally, using #[shrinkwrap(mutable)] will also derive the following traits:

  • AsMut<InnerType>
  • BorrowMut<InnerType>
  • DerefMut<Target=InnerType>

Finally, one more option is #[shrinkwrap(transformers)], which will derive some useful inherent functions for transforming the wrapped data:

  • fn transform<F>(&mut self, mut f: F) -> &mut Self where F: FnMut(&mut InnerType)
  • fn siphon<F, T>(self, mut f: F) -> T where F: FnMut(InnerType) -> T

...where transform makes it easy to chain updates on the inner value, and siphon allows you to easily move out the inner value to produce a value of a different type.

transform will have the same visibility as the inner field, which ensures that transform doesn't leak the possibility of changing the inner value (potentially in invariant-violating ways). siphon has the same visibility as the struct itself, since it doesn't provide a direct way for callers to break your data.

Cool, how do I use it?

First, add shrinkwraprs as a dependency in your Cargo.toml:

[dependencies]

shrinkwraprs = "0.3.0"

Then, just slap a #[derive(Shrinkwrap)] on any structs you want convenient-ified:

#[macro_use] extern crate shrinkwraprs;

#[derive(Shrinkwrap)]
struct Email(String);

fn main() {
    let email = Email("chiya+snacks@natsumeya.jp".into());

    let is_discriminated_email =
        email.contains("+");  // Woohoo, we can use the email like a string!

    /* ... */
}

If you have multiple fields, but there's only one field you want to be able to deref/borrow as, mark it with #[shrinkwrap(main_field)]:

#[derive(Shrinkwrap)]
struct Email {
    spamminess: f64,
    #[shrinkwrap(main_field)] addr: String
}

#[derive(Shrinkwrap)]
struct CodeSpan(u32, u32, #[shrinkwrap(main_field)] Token);

If you also want to be able to modify the wrapped value directly, add the attribute #[shrinkwrap(mutable)] as well:

#[derive(Shrinkwrap)]
#[shrinkwrap(mutable)]
struct InputBuffer {
    buffer: String
}

...
let mut input_buffer = /* ... */;
input_buffer.push_str("some values");
...

Dependencies

~0.8–1MB
~24K SLoC