I've often wondered about the differences between lazy_static
and once_cell
, and starting with Rust 1.70 the standard library is also gaining the ability to create one-time-initialized values.
Most big projects end up using one of these crates, because lazy initialization is a very convenient way to implement almost-const
global values that can't actually be const
-initialized, because they need heap memory (e.g. String
or Vec
), read files or environment variables, or use types that don't have const
initializers yet.
But how do they work? Let's spend some time looking at the differences between these three implementations.