In this article, I want to share information about crates for the that you may not know about. Most of them allow you to write less boilerplate code or improve code readability. This article will be useful for developers of all levels who write in Rust. Rust language Share in the comments crates that may be useful to other developers. In this article, a crate refers to a library that can be used as a dependency in your project. I did not copy the entire documentation for each crate, so if you are interested in a crate, I recommend checking it out on or . crates.io docs.rs Also, I did not give examples of well-known crates, such as , , , and others. serde anyhow itertools Preface You may have noticed that many projects written in Rust have a large number of primary and secondary dependencies. In my opinion, there is nothing wrong with this, and here's why. Rust has high requirements for backward compatibility with the standard library. Therefore, much of the functionality is provided in the form of third-party dependencies maintained by the community rather than by the language creators. At the same time, Rust implemented good dependency management in the form of at an early stage, which makes adding dependencies trivial. Cargo Together, this allows Rust crates to evolve faster, have less legacy code, and give users the ability to choose between different implementation approaches rather than relying solely on what the language creators included in the standard library. The above allows writing Rust crates in the Unix way, where each library does exactly one thing and does it well. The "batteries included" approach adopted, for example, in Python, worked well in the 1990s when software distribution was not as easy. Now it leads to of the Python standard library. clean-up initiatives Tap A crate that allows converting a chain of function calls from prefix notation to postfix notation. This form allows you to write more readable code. It is easiest to explain with examples. For example, a chain of calls like this: let val = last( third( second(first(original_value), another_arg) ), another_arg, ); Can be rewritten as: let val = original_value .pipe(first) .pipe(|v| second(v, another_arg)) .pipe(third) .pipe(|v| last(v, another_arg)); Or, suppose you want to sort an array "in place" using the method, which will require making the variable mutable first and then redefining the variable to make it immutable again: sort() let mut collection = stream().collect::<Vec<_>>(); collection.sort(); // potential error site: inserting other mutations here let collection = collection; // now immutable The method comes to the rescue, which passes the value of the variable by mutable reference to the closure: .tap_mut let collection = stream.collect::<Vec<_>>().tap_mut(|v| v.sort()); Accordingly, the variable can be defined only once and be immutable from the start. collection These methods do not affect the performance of the code since, at the compilation stage, these calls are optimized, and the resulting code is just as performant as the naive version. In my opinion, in both examples, the code became more readable because we got rid of unnecessary variable declarations and rewrote the function calls in a chain-like way, which allows you to read the code without jumping your eyes around the lines. These are not all the useful methods provided by this crate. For example, it contains methods that work in debug mode and are removed in release mode. There are also methods for converting between types that implement the trait. tap_x_dbg Into I recommend checking out the documentation of this crate. Strum The crate helps to get rid of boilerplate code when working with enums in Rust. The functionality is achieved through derive-type macros. For example: - implements for an enum and, therefore, the method. strum::Display std::fmt::Display to_string() -> String - implements . Therefore, it does not require memory allocation as in the case of using . strum::AsRefStr AsRef<&static str> to_string() - implements . Works similarly to the previous option. strum::IntoStaticStr From<MyEnum> for &'static str - implements and , allowing you to convert strings into enum instances. strum::EnumString std::str::FromStr std::convert::TryFrom<&str> - adds the constant , which contains the number of enum variants. strum::EnumCount COUNT: usize - implements an iterator over the enum variants. The data inside the variants will be set to . strum::EnumIter Default::default() And even more. I recommend taking a look at the . documentation of this crate An example of using the above macros: #[derive( Debug, PartialEq, strum::Display, strum::IntoStaticStr, strum::AsRefStr, strum::EnumString, strum::EnumCount, strum::EnumIter, )] enum Color { Red, Blue(usize), Green { range: usize }, } // convertions to String and &'static str assert_eq!(Color::Blue(2).to_string(), "Blue"); assert_eq!(Color::Green { range: 5 }.as_ref(), "Green"); assert_eq!(<&str>::from(Color::Red), "Red"); assert_eq!(Color::Red, Color::from_str("Red").unwrap()); assert_eq!(Color::COUNT, 3); assert_eq!( Color::iter().collect::<Vec<_>>(), vec![Color::Red, Color::Blue(0), Color::Green { range: 0 }] ); Additionally, different macros of this crate support behavior customization. For example, it is possible to change the string into which an enum instance will be converted using the attribute . #[strum(serialize = "redred")] derive_more The NewType pattern is pretty common in Rust. Sometimes you need to wrap third-party library types in our own structure: pub struct NonEmptyVec(Vec<i32>); One of the examples of using this pattern is to maintain invariants. For our structure, we can define a constructor function that checks that the internal vector is never empty: impl NonEmptyVec { pub fn new(numbers: Vec<i32>) -> Result<Self> { if numbers.is_empty() { bail!("expected non empty vector of integers") } else { Ok(Self(numbers)) } } } Therefore, the structure can only be created through a constructor that checks the required invariant. The downside of this approach is that our wrapper structure does not inherit the implementation of traits from the internal type. NonEmptyVec For example, what if we want to pass the structure to a function that takes as input? This code will not compile: IntoIterator fn collector(iter: impl IntoIterator<Item = i32>) -> Vec<i32> { iter.into_iter().collect() } #[test] fn non_emtpy_vec() -> Result<()> { let non_empty = NonEmptyVec::new(vec![1, 2, 3])?; assert_eq!(collector(non_empty), vec![1, 2, 3]); Ok(()) } We can write our own implementation: impl IntoIterator for NonEmptyVec { type Item = <Vec<i32> as IntoIterator>::Item; type IntoIter = <Vec<i32> as IntoIterator>::IntoIter; fn into_iter(self) -> Self::IntoIter { <Vec<i32> as IntoIterator>::into_iter(self.0) } } But essentially, this is boilerplate code because it duplicates the existing implementation of the trait of the internal type. This crate can help eliminate such code. We simply add the use of the derive macros for our wrapper structure: #[derive(derive_more::AsRef, derive_more::Deref, derive_more::IntoIterator, derive_more::Index)] pub struct NonEmptyVec(Vec<i32>); And check that this works: fn collector(iter: impl IntoIterator<Item = i32>) -> Vec<i32> { iter.into_iter().collect() } #[test] fn non_emtpy_vec() -> Result<()> { assert!(NonEmptyVec::new(vec![]).is_err()); let non_empty = NonEmptyVec::new(vec![1, 2, 3])?; assert_eq!(non_empty.as_ref(), &[1, 2, 3]); assert_eq!(non_empty.deref(), &[1, 2, 3]); assert_eq!(non_empty[1], 2); assert_eq!(collector(non_empty), vec![1, 2, 3]); Ok(()) } As you see, we automatically restored the implementation of several useful traits. This crate contains macros for generating conversion traits ( , , , etc), formatting traits ( -like), operator traits ( , , etc), useful methods ( , , etc). From IntoIterator AsRef Display Add Index Constructor Unwrap derive_builder One of the popular patterns in Rust is the builder pattern [ , ]. This pattern is convenient when you need to create a complex structure with many fields. 1 2 For example, let's say we have several functions that perform some complex work and return a result of - a structure with many optional fields - and then we want to test the functions in a unit test: Calculation #[derive(Debug, Eq, PartialEq)] struct Calculation { a: Option<i32>, b: Option<i32>, c: Option<i32>, d: Option<i32>, // ... can be more optional fields } fn qwe() -> Calculation { // does complex calculation Calculation { a: Some(1), b: None, c: None, d: None, } } fn asd() -> Calculation { // does complex calculation Calculation { a: Some(6), b: None, c: None, d: Some(7), } } fn zxc() -> Calculation { // does complex calculation Calculation { a: None, b: Some(2), c: Some(3), d: None, } } #[test] fn test() -> Result<()> { assert_eq!( qwe(), Calculation { a: Some(1), b: None, c: None, d: None, } ); assert_eq!( asd(), Calculation { a: Some(6), b: None, c: None, d: Some(7), } ); assert_eq!( zxc(), Calculation { a: None, b: Some(2), c: Some(3), d: None, } ); Ok(()) } Using derive_more crate, we can simplify the code and make it more concise: #[derive(Debug, Eq, PartialEq, Default, derive_builder::Builder)] // setters calls can be chained, each call clones builder #[builder(pattern = "immutable")] // if field not set then it would be default (None in our case) #[builder(default)] // setter method accepts T as argument and field value would be Some(T) #[builder(setter(strip_option))] struct Calculation { a: Option<i32>, b: Option<i32>, c: Option<i32>, d: Option<i32>, // ... can be more optional fields } fn qwe() -> Calculation { /* same as before */ } fn asd() -> Calculation { /* same as before */ } fn zxc() -> Calculation { /* same as before */ } The crate generated a builder structure named with setters for each field. CalculationBuilder Now the test can be rewritten much shorter: #[test] fn derive_builder() -> Result<()> { let builder = CalculationBuilder::default(); assert_eq!(qwe(), builder.a(1).build()?); assert_eq!(asd(), builder.a(6).d(7).build()?); assert_eq!(zxc(), builder.b(2).c(3).build()?); Ok(()) } As you can see, now we can assign values only to the required fields, and the test has become much shorter. If there were more optional fields in the structure, the gain in brevity and readability would be even higher. This is just one example of using the pattern which is facilitated by this crate. The crate also supports field validation in the method and several types of builders (owned, mutable, immutable). builder build Perhaps the only downside of this crate, in my opinion, is that the generated method always returns , even when consists only of optional fields, as in our case. build Result<T> T insta Library for snapshot testing. Snapshot represents the expected result of a test, usually stored in a separate file. The library provides a command-line utility for easy snapshot updates. The crate offers many features that can be found in the official . guide One of the useful features, in my opinion, is . It allows testing values with random or non-deterministic fields order, such as : redactions HashSet #[derive(serde::Serialize)] pub struct User { id: Uuid, username: String, flags: HashSet<&'static str>, } #[test] fn redactions() { let user = User { id: Uuid::new_v4(), username: "john_doe".to_string(), flags: maplit::hashset! {"zzz", "foo", "aha"}, }; insta::assert_yaml_snapshot!(user, { ".id" => "[uuid]", // make hashset order deterministing ".flags" => insta::sorted_redaction() }); } For this test, a snapshot was automatically generated with the following contents: snapshots/insta__tests__redactions.snap --- source: src/bin/insta.rs expression: user --- id: "[uuid]" username: john_doe flags: - aha - foo - zzz enum_dispatch Rust supports polymorphism through static and dynamic dispatch of traits. If you have used dynamic dispatch, you know that it can negatively impact the performance of a program, as the trait implementation is looked up through a vtable at runtime. This trait allows you to turn dynamic dispatch into static dispatch using an enum. Suppose we have such a trait and its implementations: pub trait ReturnsValue { fn return_value(&self) -> usize; } pub struct Zero; impl ReturnsValue for Zero { fn return_value(&self) -> usize { 0 } } pub struct Any(usize); impl ReturnsValue for Any { fn return_value(&self) -> usize { self.0 } } In this example, we are using dynamic dispatch: #[test] fn derive_dispatch_dynamic() { let values: Vec<Box<dyn ReturnsValue>> = vec![Box::new(Zero {}), Box::new(Any(5))]; assert_eq!( values .into_iter() .map(|dispatched| dispatched.return_value()) .collect::<Vec<_>>(), vec![0, 5] ); } Now let's use this trait: #[enum_dispatch::enum_dispatch] pub trait ReturnsValue { fn return_value(&self) -> usize; } // trait implementations are same #[enum_dispatch::enum_dispatch(ReturnsValue)] pub enum EnumDispatched { Zero, Any, } #[test] fn derive_dispatch_static() { let values = vec![EnumDispatched::Zero(Zero {}), EnumDispatched::Any(Any(5))]; assert_eq!( values .into_iter() .map(|dispatched| dispatched.return_value()) .collect::<Vec<_>>(), vec![0, 5] ); } Thus, the crate generated the implementation of the trait for the enum. The creators of the crate conducted performance tests and found that such an implementation can speed up trait usage up to 10-12 times. ReturnsValue EnumDispatched In my opinion, the main disadvantage of this library is that you can only . Since it is necessary to apply the macro directly to the trait (so that can read the function signatures of the trait). Accordingly, it is possible to apply the macro to a trait only in your crate, which you can edit. generate an implementation for a trait declared in your crate #[enum_dispatch::enum_dispatch] enum_dispatch paste The crate allows concatenating identifiers at compile-time without using nightly. This is useful when writing macros to create arbitrary identifiers using macro variables and static literal identifiers. Here's a shortened example from the crate's readme. This macro will create an block for a type with the name and create getter methods for each . impl $name $field macro_rules! make_a_struct_and_getters { ($name:ident { $($field:ident),* }) => { // ... // Build an impl block with getters. This expands to: // impl S { // pub fn get_a(&self) -> &str { &self.a } // pub fn get_b(&self) -> &str { &self.b } // pub fn get_c(&self) -> &str { &self.c } // } paste! { impl $name { $( pub fn [<get_ $field>](&self) -> &str { &self.$field } )* } } } } make_a_struct_and_getters!(S { a, b, c }); fn call_some_getters(s: &S) -> bool { s.get_a() == s.get_b() && s.get_c().is_empty() } either The general-purpose enum has two variants and . It has a variety of methods and traits for convenient work using this enum. Either Left Right use either::Either; #[test] fn test() { let values = vec![ Either::Left(1), Either::Right(true), Either::Left(10), Either::Right(false), ]; assert_eq!( values .into_iter() .map(|int_or_bool| -> Either<i32, bool> { let int = either::try_left!(int_or_bool); Either::Left(int * 2) }) .map(|int_or_bool| { either::for_both!(int_or_bool, s => s.to_string()) }) .collect::<Vec<_>>(), ["2", "true", "20", "false"] ); } num The collection of numeric traits and types. Includes generics for numbers, big integers, complex numbers, and so on. use anyhow::{anyhow, Result}; use num::*; use std::fmt::Display; fn bounds_to_string<N: Bounded + Display>(number: N) -> String { format!( "value {} min is {} max is {}", number, N::min_value(), N::max_value() ) } #[test] fn bounds() { assert_eq!(bounds_to_string(12u8), "value 12 min is 0 max is 255"); assert_eq!( bounds_to_string(33i16), "value 33 min is -32768 max is 32767" ); } fn num_operations<N: Num>(a: &str, b: N) -> Result<N> { let a = N::from_str_radix(a, 10).map_err(|_| anyhow!("could not conert value"))?; let value = a + b - N::one(); Ok(if value.is_zero() { value } else { value * (N::one() + N::one()) }) } #[test] fn test_num_operations() -> Result<()> { assert_eq!(num_operations("2", 10i32)?, 22i32); assert_eq!(num_operations("-5", 6i8)?, 0i8); Ok(()) } #[test] fn greatest_common_divisor() -> Result<()> { assert_eq!(num::integer::gcd(25u8, 15u8), 5); assert_eq!(num::integer::gcd(1024i32, 65536i32), 1024); Ok(()) } thiserror The crate provides a macro for implementing the trait on structs and enums. std::error::Error From the error-handling perspective, there are two types of crates: libraries and applications. A library is created as a third-party dependency that will be used in applications. For library crates, it is important that the calling code can check, if necessary, what type of error occurred in the library code, and implement different behaviors for different types of errors. For example, ignore I/O errors, but panic on data format errors. For applications, the specific type of errors is usually not important, so application functions usually return a type, as allows convenient conversion of any error into the type using the operator or trait. More details can be found here: (a slightly old article) and (a newer one). Result<T, anyhow::Error> anyhow anyhow::Error ? From [1] [2] This crate is mainly used for convenient error implementation in library crates. Usage example: #[derive(thiserror::Error, Debug)] pub enum SomeError { #[error("io error")] Io(#[from] std::io::Error), #[error("int parsing error")] ParseInt(#[from] std::num::ParseIntError), #[error("unknown error")] General(#[from] anyhow::Error), } /// library func fn int_error(s: &str) -> Result<i32, SomeError> { let num = i32::from_str_radix(s, 10)?; Ok(num + 2) } #[test] fn test() { // application code assert!(matches!(int_error("abc").unwrap_err(), SomeError::ParseInt(_))); assert!(matches!( std::io::Error::new(std::io::ErrorKind::Other, "oh no!").into(), SomeError::Io(_) )); } In the example above, the error was converted to the enum. Without this crate, we would have had to manually write all these conversions. std::num::ParseIntError SomeError::ParseInt rayon This crate makes it easier to use parallelism in Rust. It is suitable for converting sequential iterators into parallel ones. It guarantees the absence of data races. Parallel iterators adapt their behavior at runtime for maximum performance. use rayon::prelude::*; fn sum_of_squares(input: &[i32]) -> i32 { input .par_iter() // <-- just change that! .map(|&i| i * i) .sum() } In the example above, a sequential iterator was turned into a parallel one by simply changing to . iter() par_iter() crossbeam The crate provides a set of tools for concurrent programming: atomics, data structures, memory management, thread synchronization, and more. For example, the implementation of channels in this crate is more performant compared to std channels (according to the developers) and allows multiple producers and multiple consumers (multi-producer multi-consumer), unlike std channels, which only allow a single consumer (multi-producer single-consumer). async_trait The crate allows defining traits with async functions. Currently, Rust does not support async traits at the language syntax level, so you can use the macro provided by this crate with the same name as the trait. You can read more about why async fn in traits are hard in this . article use async_trait::async_trait; #[async_trait] trait Advertisement { async fn run(&self); } struct Modal; #[async_trait] impl Advertisement for Modal { async fn run(&self) { self.render_fullscreen().await; for _ in 0..4u16 { remind_user_to_join_mailing_list().await; } self.hide_for_now().await; } } The macro changes the function signatures to return . Pin<Box<dyn Future + Send + 'async_trait>> fs-err This crate contains wrapper functions with human-readable errors for functions from . std::fs If you have used functions from (such as or ), you may have noticed that in case of an error, the error message is not very informative: std::fs read_to_string write let content = File::open("file-not-exist.txt")?; let config = File::open("config-not-exist.txt")?; // error message would be: // The system cannot find the file specified. (os error 2) With the crate, we can get more detailed error messages, such as which file does not exist: fs-err failed to open file `config-not-exist.txt` caused by: The system cannot find the file specified. (os error 2) tempfile This crate provides an API for creating temporary files and directories. // Write let mut tmpfile: File = tempfile::tempfile().unwrap(); write!(tmpfile, "Hello World!").unwrap(); // Seek to start tmpfile.seek(SeekFrom::Start(0)).unwrap(); // Read let mut buf = String::new(); tmpfile.read_to_string(&mut buf).unwrap(); assert_eq!("Hello World!", buf); bincode This crate provides encoding and decoding of structures to and from byte arrays. It uses a compact data format that is suitable for storage on disk and for exchanging data between systems with different processor architectures. use anyhow::Result; use serde::{de::DeserializeOwned, Deserialize, Serialize}; #[derive(Serialize, Deserialize, PartialEq, Debug)] struct Entity { number: i8, name: String, } fn check<T>(value: &T, expected_size: usize) where T: Serialize + DeserializeOwned + PartialEq + std::fmt::Debug, { let encoded: Vec<u8> = bincode::serialize(&value).unwrap(); assert_eq!(encoded.len(), expected_size); let decoded: T = bincode::deserialize(&encoded[..]).unwrap(); assert_eq!(value, &decoded); } #[test] fn test() -> Result<()> { let first_size = 9; // i8 + u64 for string length check(&Entity {number: 1, name: "".to_owned()}, first_size); let second_size = 15; // i8 + u64 for string length + 6 bytes of string check(&Entity {number: 2, name: "string".to_owned()}, second_size); Ok(()) } maplit The crate provides macros for generating container types from . It is largely a matter of personal preference, as containers already have and methods. There is an open on this topic. std from from_iter RFC let a = btreemap! { "a" => vec![1, 2, 3], "b" => vec![4, 5, 6], "c" => vec![7, 8, 9], }; // vs let b = BTreeMap::from([ ("a", vec![1, 2, 3]), ("b", vec![4, 5, 6]), ("c", vec![7, 8, 9]), ]); indexmap An ordered hash map that preserves the insertion order of elements. This means that iterating over the elements of the hash map will occur in the same order as the insertion order of the elements. This order is preserved until you call the method. The hash map supports searching for elements by key and by numeric index (like an array) and fast iteration over elements. All these properties arise from the fact that it a vector of key-value pairs and a hash table mapping keys' hashes to their indices in the vector. It's worth using if these properties fit your use case. remove stores getset This crate will be appreciated by former Java programmers. The crate contains procedural macros for generating getter and setter methods. use getset::{CopyGetters, Getters, MutGetters, Setters}; #[derive(Getters, Setters, MutGetters, CopyGetters, Default)] pub struct Foo<T> where T: Copy + Clone + Default, { /// Doc comments are supported! /// Multiline, even. #[getset(get, set, get_mut)] private: T, /// Doc comments are supported! /// Multiline, even. #[getset(get_copy = "pub", set = "pub", get_mut = "pub")] public: T, } fn main() { let mut foo = Foo::default(); foo.set_private(1); (*foo.private_mut()) += 1; assert_eq!(*foo.private(), 2); } mockall This crate provides automatic mock object generation for (almost all) traits and structures. These objects can be used in unit tests instead of objects of the original type, which can make it easier to write high-level unit tests or test complex edge cases. #[cfg(test)] use mockall::{automock, predicate::*}; #[cfg_attr(test, automock)] trait CalcTrait { fn foo(&self, x: u32) -> u32; } fn calculation(calc: impl CalcTrait, x: u32) -> u32 { calc.foo(x) } #[test] fn test() { let mut mock = MockCalcTrait::new(); mock.expect_foo().with(eq(4)).times(1).returning(|x| x + 1); assert_eq!(5, calculation(mock, 4)); } Mock objects can be automatically generated using the attribute macro. However, it has its limitations, so sometimes you have to use the procedural macro with a more manual implementation of mock objects. #[automock] mock! QuickCheck QuickCheck is a framework for property-based testing. It allows testing code with a large number of arbitrary input data. If an error is found, it automatically finds the minimal test case to reproduce the error. #[cfg(test)] mod tests { fn reverse<T: Clone>(xs: &[T]) -> Vec<T> { let mut rev = vec!(); for x in xs { rev.insert(0, x.clone()) } rev } #[quickcheck] fn double_reversal_is_identity(xs: Vec<isize>) -> bool { xs == reverse(&reverse(&xs)) } } proptest Like , is a property-based testing framework. However, it has a in contrast to , although for complex data, it may take significantly longer to run than . quickcheck proptest more flexible input data generation quickcheck quickcheck proptest! { #[test] fn doesnt_crash(s in "\\PC*") { parse_date(&s); } #[test] fn parses_date_back_to_original(y in 0u32..10000, m in 1u32..13, d in 1u32..32) { let result = parse_date(&format!("{:04}-{:02}-{:02}", y, m, d)).unwrap(); prop_assert_eq!((y, m, d), result); } } heck A library for converting text into various commonly used variable naming styles, such as , , and others. CamelCase snake_case For example, when you use the attribute in the library, it the functionality of . rename_all sqlx uses heck use heck::ToShoutyKebabCase; #[test] fn test() { assert_eq!("i am very angry!".to_shouty_kebab_case(), "I-AM-VERY-ANGRY"); } num_cpus A small crate that helps determine the number of physical CPU cores or the number of parallel tasks that can be efficiently executed on the system. For example, I used it to when going through the tutorial . determine the number of threads Ray Tracing in One Weekend humantime This library provides a formatter and parser for in a human-readable format. It also has integration with through the crate. This allows, for example, specifying values in the application/service config in a readable format instead of using a unit of measurement in the variable name, reducing the likelihood of errors: std::time::{Duration, SystemTime} serde humantime-serde Duration # for example instead of this: timeout_mins: 120 # you can write this: timeout: 2 hours Usage example: use serde::{Deserialize, Serialize}; use std::time::Duration; #[test] fn format() { let duration = Duration::new(9420, 0); let as_str = "2h 37m"; assert_eq!(humantime::format_duration(duration).to_string(), as_str); assert_eq!(humantime::parse_duration(as_str), Ok(duration)); } #[derive(Serialize, Deserialize)] struct Foo { #[serde(with = "humantime_serde")] timeout: Duration, } #[test] fn serde() { let input = r#" { "timeout": "3 days 1hour 12min 5s" } "#; let foo: Foo = serde_json::from_str(input).unwrap(); assert_eq!(foo.timeout, Duration::new(263525, 0)); } overload This crate provides a macro for easier implementation of trait operators. This crate is useful when a more complex implementation of an operator is needed. In the example below, we generated the addition of two different types, which actually works as . For simpler cases, the crate will be more suitable. a * b + 1 derive_more use overload::overload; use std::ops; #[derive(PartialEq, Debug)] struct A { v: i32, } #[derive(PartialEq, Debug)] struct B { v: i32, } // ? below generate operator for A and &A values overload!((a: ?A) + (b: ?B) -> B { B { v: a.v * b.v + 1 } }); #[test] fn test() { assert_eq!(&A { v: 3 } + B { v: 5 }, B { v: 16 }); } enum-iterator Macros for generating iterators over the values of an enum or structure. use enum_iterator::{all, first, last, next, Sequence}; use itertools::Itertools; #[derive(Debug, PartialEq, Sequence)] enum Direction { Left, Middle, Right, } #[test] fn test_enum() { use Direction::*; assert_eq!(all::<Direction>().collect_vec(), vec![Left, Middle, Right]); assert_eq!(first::<Direction>(), Some(Left)); assert_eq!(last::<Direction>(), Some(Right)); assert_eq!(next(&Middle), Some(Right)); } #[derive(Debug, PartialEq, Sequence)] struct Foo { a: bool, b: u8, } #[test] fn test_struct() { let expected_number_of_elements = 512; assert_eq!( enum_iterator::cardinality::<Foo>(), expected_number_of_elements ); assert_eq!(first::<Foo>(), Some(Foo { a: false, b: 0 })); assert_eq!(last::<Foo>(), Some(Foo { a: true, b: 255 })); } cfg-if This crate provides a convenient declaration of items that depend on a large number of configurations in the form of if-else expressions. #[cfg] cfg_if::cfg_if! { if #[cfg(unix)] { fn foo() { /* unix specific functionality */ } } else if #[cfg(target_pointer_width = "32")] { fn foo() { /* non-unix, 32-bit functionality */ } } else { fn foo() { /* fallback implementation */ } } } arrayref The macros for conveniently creating arrays from slices. let addr: &[u8; 16] = ...; let mut segments = [0u16; 8]; // array-based API for i in 0 .. 8 { let mut two_bytes = [addr[2*i], addr[2*i+1]]; segments[i] = read_u16_array(&two_bytes); } // array-based API with arrayref for i in 0 .. 8 { segments[i] = read_u16_array(array_ref![addr, 2*i, 2]); } educe This crate provides procedural macros for faster, flexible, and declarative implementation of traits from the standard library, like , , , and so on. The flexibility lies in the ability to exclude fields from implementation, including trait bounds, and so on. Debug Eq Ord Deref #[derive(educe::Educe)] // note `new` below: generate `new()` that calls Default #[educe(Default(new))] #[derive(Debug, PartialEq)] struct Struct { #[educe(Default = 3)] f1: u8, #[educe(Default = true)] f2: bool, #[educe(Default = "Hello")] f3: String, } #[test] fn test() { let expected = Struct { f1: 3, f2: true, f3: String::from("Hello"), }; assert_eq!(Struct::default(), expected); assert_eq!(Struct::new(), expected); } derivative This crate also provides macros for implementing traits from the standard library, similar to . However, the last update of this library was in January 2021. educe #[derive(Derivative)] #[derivative(PartialEq)] struct Foo { foo: u8, #[derivative(PartialEq="ignore")] bar: u8, } assert!(Foo { foo: 0, bar: 42 } == Foo { foo: 0, bar: 7}); assert!(Foo { foo: 42, bar: 0 } != Foo { foo: 7, bar: 0}); chronoutil In Rust, the type is used to manipulate dates, which represent a fixed number of seconds and nanoseconds. The has constructor functions named , , , but it doesn't have a constructor because it is a relative value that cannot be expressed in seconds. Therefore, if you want to manipulate dates in more human-readable units, such as adding a month or a year, you can use the tools provided by this crate. Duration chrono::Duration weeks days hours month let delta = RelativeDuration::months(1) + RelativeDuration::days(1); assert_eq!( NaiveDate::from_ymd(2021, 1, 28) + delta, NaiveDate::from_ymd(2021, 3, 1) ); assert_eq!( NaiveDate::from_ymd(2020, 1, 28) + delta, NaiveDate::from_ymd(2020, 2, 29) ); References https://www.reddit.com/r/rust/comments/uevmnx/what_crates_would_you_consider_essential/ https://www.reddit.com/r/rust/comments/ylp4nz/what_crates_are_considered_as_defacto_standard/ https://blessed.rs/crates https://lib.rs/ https://crates.io/crates?sort=recent-downloads https://www.reddit.com/r/rust/comments/nuq1ix/whats_your_favourite_underrated_rust_crate_and_why/ Also published . here