1 unstable release

0.1.0 Nov 9, 2020

#775 in Concurrency

Apache-2.0 OR MIT OR Zlib

23KB
213 lines

lazy_id

Build Status codecov Docs Latest Version

Provides lazy_id::Id, a thread-safe 64-bit id that only initializes itself to a specific value when you use it rather than when you create it. It works with no_std (without liballoc either), is entirely lock-free, currently supports versions as far back as 1.34.0, and has zero dependencies other than libcore.

Usage

use lazy_id::Id;
struct Thing {
    id: Id,
    // other fields, ...
}
// Now this function can be const, allowing use in statics
const fn new_thing() -> Thing {
    Thing { id: Id::lazy(), /* ... */ }
}
static C: Thing = new_thing();
// also works for non-static without meaningful overhead
let a = new_thing();
let b = new_thing();
// `Id` implements `PartialEq`, and many other useful traits.
assert!(a.id != b.id && a.id != C.id);

Why would this be helpful?

If you need a unique per-instance Id for your type, usually the approach is a global atomic that you increment each time you allocate an id.

The only problem here is that now if you want to store your type in a static of some sort, you need to use OnceCell, lazy_static, or some other lazy initialization crate. These crates are fine and wouldn't be expected to cause perfomance problems the majority of the time, but they can be frustrating if you didn't already need them, and can be extremely undesirable to force this on users of your library.

They also are a dependency you want to avoid if previously you were no_std — You essentially need to hold a lock while doing generic thread-safe lazy init, and doing that with no_std requires a spinlock, which is... not great (this is why once_cell requires std for the sync functionality).

So, why doesn't lazy_id have this problem? Well, we're not doing any sort of generic initialization. Ours is extremely specific and concrete, and was designed to avoid taking a lock. This means we can easily avoid the std dep without any spinning.

No runtime deps