Rust has a Cow
(clone-on-write) smart pointer in its standard library. Understanding how to use it is essential to write idiomatic and ergonomic Rust code.
In a nutshell:
- it allows to combine usage of owned and borrowed data in a single abstraction, which leads to better ergonomics and minimize performance penalties as much as possible;
- it encloses and provides immutable access to borrowed data, and clones the data lazily when mutation or ownership is required.
use std::borrow::Cow;
fn describe(error: &Error) -> Cow<'static, str> {
match *error {
// Returning &'str - a borrowed reference to static str.
Error::NotFound => "Error: Not found".into(),
// Returning String - an owned String allocated in heap.
Error::Custom(e) => format!("Error: {}", e).into(),
}
}
For better understanding Cow
purpose, design, limitations and use cases read through:
- Official
Cow
docs - Pascal Hertleif: The Secret Life of Cows
- Yashodhan Joshi: Using
Cow
in Rust for efficient memory utilization - Konstantin Grechishchev: 6 things you can do with the Cow 🐄 in Rust 🦀
- Deref vs AsRef vs Borrow vs Cow
- &str docs
- dealing with ownerchip and borrowing in public interfaces
beef
crate provides alternative Cow
types, being faster and leaner.
There are two versions of
Cow
exposed by this crate:
beef::Cow
is 3 words wide: pointer, length, and capacity. It stores the ownership tag in capacity.beef::lean::Cow
is 2 words wide, storing length, capacity, and the ownership tag all in one word.Both versions are leaner than the
std::borrow::Cow
:use std::mem::size_of; const WORD: usize = size_of::<usize>(); assert_eq!(size_of::<std::borrow::Cow<str>>(), 4 * WORD); assert_eq!(size_of::<beef::Cow<str>>(), 3 * WORD); // Lean variant is two words on 64-bit architecture #[cfg(target_pointer_width = "64")] assert_eq!(size_of::<beef::lean::Cow<str>>(), 2 * WORD);
Read implementation details and design insights in its README.
Estimated time: 1 day
Write a simple program which prints out the path to its configuration file. The path should be detected with the following precedence:
- if
--conf
command line argument is specified (error if empty) then use it. <--- highest priority - if
APP_CONF
env var is specified (and not empty) then use it; - default path is
/etc/app/app.conf
;
If neither APP_CONF
env var nor --conf
command line argument is specified, then no allocation should happen for path detection.
After completing everything above, you should be able to answer (and understand why) the following questions: