Rust has a very rich ecosystem, with fantastic crates like serde, tokio, axum, clap and many others.
But there are quite a lot of crates that usually don't get much mention, but nevertheless they are pretty cool!
The list focuses on crates that fit in the Rust Patterns category, hopefully you'll try some of these out!
I like pushing invariants into the type system, and make invalid states impossible to represent. That's one of the things that make Rust fun!
I've long searched for a good crate that provides non-empty collections, notably Vec. There is nonempty, which is the most popular, but it uses a representation like this:
Sure, this makes it literally impossible to construct a non-empty Vec, but at what cost?
- Vec<T> to NonEmptyVec<T> is O(n). Not zero cost.
- Since it doesn't deref to &[T] anymore, it's just much harder to compose with everything else...
- The only advantage of this layout is that you can store 1 element without allocation. Cool, but I've personally never needed that.
That's when I discovered mitsein. This library:
- Contains dozens of non-empty collections: Vec, Hash, ...
- Includes feature flags for popular crates, like indexmap, serde, smallvec, ...
- Has extremely good documentation. Every API is extensively documented.
- Uses the zero-cost layout #[repr(transparent)] struct NonEmptyVec<T>(Vec<T>)
Here's a small example from the library:
Rust's derive feature is incredibly powerful, with crates like serde, strum, derive_more, num_traits and many others providing custom derive macros.
Especially for people who like newtypes, it's not uncommon to have 10+ derives applied to the same item. At which point rustfmt will place every derive onto its own line. Then before you know it, the #[derive] attribute is twice the size of whatever item you are applying it to!
Say no more. With derive_aliases, you can define an alias that expands into a bunch of derives, replacing code like this:
With this:
Ever wrote a test with a bunch of calls to assert! - and the test fails. But only the first panic is shown! Oh no! You now need to manually re-run the test and comment out the first assert! to get information about the 2nd one.
This experience isn't the best. The assert2 crate is here to save us all, it provides the check! macro. It is like assert! but it doesn't fail the test immediately. Instead, all failures of check! are collected and then reported all at once!
The assertion macros in this crate provide more descriptive and helpful error messages, 🌈 with color! 🌈, and detailed information about what exactly failed!
With the error:
assert2 even has diffs!
With the error:
Where have you been my whole life, assert2??
The cfg_aliases! crate exports a macro that you invoke inside of the build.rs file. It allows you to have nice, rememberable names for configuration predicates.
Let's say your program first reads config from ~/.config/foo.toml, and if the user's current working directory contains .foo/config.toml it also reads that.
When we read the first config file, we want to create a Config struct with all values set to the default if not specified. Reading the 2nd config file, we want to record only the values the user has set.
The struct-patch crate makes that process easy and fun, with a derive macro:
By deserializing the 2nd config file into ConfigPatch, you can then apply only the overrides that the user has set. Handy!
It's not uncommon to have integers that we want to limit to a certain range, for example a Percentage that can only hold a value in the range 1..=100. The crate deranged contains special wrapper around every integer kind from the standard library, and you can specify them like RangedI8::<1, 100>
Have you ever wished you can just create methods on types from other crates? Let's say you call result.map_err(Into::into) a lot, and you wish the standard library had this functionality built-in.
You can do that! This pattern is called "Extension traits" - you define a trait with the methods that you want (err_into), and then implement it for the type:
Needless to say, it's really verbose. You have to repeat the signature twice (😱), and since generics are usually involved, it's common to amount to a lot of boilerplate.
easy-ext cut down this boilerplate in half:
While non-empty collections are useful, sometimes we have a list of elements that can only ever be 2 or more. Or, between 5 and 10. The bounded-vec crate provides exactly this type.
For example you can have a BoundedVec<u8, 2, 4> that holds 2, 3 or 4 bytes:
If you've ever written a library and want to have 100% documentation on everything, then you might have written errors that look like this:
We are repeating the same content! That's so much noise. We have an extra unnecessary line for every variant. The displaydoc crate allows us to avoid repeating ourselves - the documentation comments are used to implement the Display trait!
Writing multi-line string literals in rust can be... a bit of a pain!
On one hand, you have to sacrifice on indentation:
Another option is to increase syntax pollution levels to dangerous levels:
What if we want the best of both worlds? We want:
- Gets formatted by rustfmt to match indentation levels of surrounding code
- It is readable, without syntactic noise
- Composes nicely with macros that use the formatting syntax
docstr! is all of those things, and more!
The crate decorum provides drop-in replacements for the standard library types. These replacements have features like:
- Reals can't be NaN. ExtendedReals can't be NaN or infinite
- Total, Real and ExtendedReal can be hashed and have total ordering
- These types can either return a Result or panic when they enter an illegal state!
- All the great methods you'll find on standard library's floats are found here, too
How often do you have a list of strings like ["a", "b", "c"] and want to join them to become "a, b and c", or "a, b, or c"?
The literator crate provides an extension trait for Iterators that formats the iterator nicely for you, as well as many other utilities for working with iterators of strings.