[−][src]Struct lazy_id::Id
A thread-safe lazily-initialized 64-bit ID.
This is useful if you have a structure which needs a unique ID, but don't
want to return it from a const fn, or for callers to be able to use it in
statics, without requiring that they lazily initialize the whole thing, e.g.
via lazy_static
/ OnceCell
/ etc.
The Id
type initializes exactly once, and never returns a duplicate — the
only Id
s which might have the same value as others are ones that come from
Id
's impl of Clone
or Id::from_raw_integer
.
It supports most traits you'd want, including Hash
, Ord
, Clone
,
Deref<Target = u64>
, PartialEq<u64>
(and u64
has PartialEq<Id>
),
Debug
, Display
, Default
(same as Id::new
)...
Id
's initialization is entirely lock-free and uses only relaxed atomic
operations (nor is anything stronger needed). The fast path of Id::get
is just a Relaxed
atomic load, which is the same cost as a non-atomic load
(on platforms that support 64-bit atomics efficiently, anyway).
Example
use lazy_id::Id; struct Thing { id: Id, // other fields, ... } // Now this function can be const, which can let // callers avoid needing to use `lazy_static`/`OnceCell` // for constants of your type const fn new_thing() -> Thing { Thing { id: Id::lazy(), /* ... */ } } static C: Thing = new_thing(); let a = new_thing(); let b = new_thing(); assert!(a.id != b.id && a.id != C.id);
FAQs
(Okay, nobody's asked me any of these, but it's a good format for misc. documentation notes).
Are Id
s unique?
Across different runs of your program? No. Id generation order is deterministic and occurs in the same order every time.
Within a single run of your program? Yes, with two caveats:
-
Id
implementsClone
by producing otherId
s with the same numeric value. This seems desirable, as it makes theId
behave as if it had been produced eagerly, and more like a normal number. -
The function
Id::from_raw_integer
forces the creation of anId
with a specific numeric value, which may or may not be a value which has been returned already, and may or may not be one we'll return in the future. This function should be used with care.
It's intentionally okay for unsafe code to assume Id
s that it creates
through Id::new
/Id::lazy
/Id::LAZY_INITIALIZER
will all have
distinct values.
You mentioned a counter, what about overflow?
The counter is 64 bits, so this will realistically never happen. If we
assume Id::new
takes 1ns (optimistic), this would take 292 years.
Attempting to bring this down with ✨The Power Of Fearless Concurrency✨ would
probably not change this much (or would make it slower), due to that
increasing contention on the counter, but who knows.
If we do overflow, we abort
. The abort
(and not panic
) is because it
is global state that is compromised, and so all threads need to be brought
down. Additionally, I want lazy_id::Id
usable in cases where unsafe code
can rely on the values returned being unique (so long as they can ensure
that none of them came from Id::from_raw_integer
).
What is seq=
in the "{:?}"
output of an Id
?
Id debug formats like "Id(0xhexhexhex; seq=32)"
. The seq
value is a
monotonically increasing value that can help identify the order Id
s were
initialized in, but mostly is a vastly more readable number than the real
number, which makes it good for debug output.
I may expose a way to convert between id
values and seq
values in the
future, let me know if you need it.
For a little more explanation: By default, ids are mixed somewhat, which helps discourage people from using them as indexes into arrays or assuming they're sequential, etc (they aren't — they're just monotonic). It also might help them be better hash keys, but with a good hash algo it won't matter.
Implementations
impl Id
[src]
pub const fn lazy() -> Self
[src]
Create an Id
that will be automatically assigned a value when it's
needed.
use lazy_id::Id; struct Thing { id: Id, // other fields, ... } // Now this function can be const, which can let // callers avoid needing to use `lazy_static`/`OnceCell` // for constants of your type const fn new_thing() -> Thing { Thing { id: Id::lazy(), /* ... */ } } static C: Thing = new_thing(); let a = new_thing(); let b = new_thing(); assert!(a.id != b.id && a.id != C.id);
If you are not in a const context or other situation where you need to
use lazy initialization, Id::new
is a little more efficient.
If you're in an array literal initializer, Self::LAZY_INITIALIZER
may
work better for you. Note that using any of these vec!
literal will
produce a vector with n
clones of the same Id
, as it invokes
clone()
— e.g. vec![Id::lazy(); n]
should probably be written as,
(0..n).map(|_| Id::lazy()).collect::<Vec<_>>()
, which will do the
right thing (Note that because this isn't const, using Id::new()
in
the map
function would be even better, but isn't the point). This is a
problem inherent with vec!
, and other types have it as well.
pub fn new() -> Self
[src]
Create an Id
which has been initialized eagerly.
When you don't need the const
, use this, as it is more efficient.
See Id::lazy
for the lazy-init version, which is the main selling
point of this crate.
Example
let a = Id::new(); let b = Id::new(); assert_ne!(a, b);
pub const LAZY_INITIALIZER: Self
[src]
Equivalent to Id::lazy()
but usable in situations like
static array initializers (or non-static ones too).
For example, the fails because Id
isn't Copy
, and even if it worked
for clone()
, it would produce the wrong value.
// Doesn't work :( static ARR: [Id; 2] = [Id::lazy(); 2];
Using Id::LAZY_INITIALIZER
, while awkward, works fine (but only in
rust versions above 1.38.0+)
static ARR: [Id; 2] = [Id::LAZY_INITIALIZER; 2]; assert_ne!(ARR[0], ARR[1]);
This API is only present for these sorts of cases, and shouldn't be used
when either Id::new
or Id::lazy
works.
pub fn get(&self) -> u64
[src]
Returns the value of this id, lazily initializing if needed.
Often this function does not need to be called explicitly.
Example
let a = Id::lazy(); let b = Id::lazy(); assert_ne!(a.get(), b.get());
pub fn get_nonzero(&self) -> NonZeroU64
[src]
Initialized id values are never zero, so we can provide this trivially.
It's unclear how useful it is, although we accept a NonZeroU64
in
Id::from_raw_integer
, and this makes that easier to call.
Example
let a = Id::new(); let manual_clone_of_a = Id::from_raw_integer(a.get_nonzero()); assert_eq!(a, manual_clone_of_a);
pub const fn from_raw_integer(id: NonZeroU64) -> Self
[src]
Create an id with a specific internal value. Something of an escape hatch.
Internally, we reserve 0 as a sentinel that indicates the Id has not
been initialized and assigned a value yet. This is why we accept a
NonZeroU64
and not a u64
. That said, Id::get_nonzero
or
NonZeroU64::from(id)
avoid this being too annoying when the input
was from an Id
originally
Caveats
This function should be used with care, as it compromises the uniqueness
of Id
— The resulting Id
may be one with a value we use in the
future, or have used in the past.
Example
let v = Id::from_raw_integer(NonZeroU64::new(400).unwrap()); assert_eq!(v.get(), 400);
Trait Implementations
impl AsRef<u64> for Id
[src]
impl Borrow<u64> for Id
[src]
impl Clone for Id
[src]
fn clone(&self) -> Self
[src]
fn clone_from(&mut self, source: &Self)
1.0.0[src]
impl Debug for Id
[src]
impl Default for Id
[src]
impl Deref for Id
[src]
impl Display for Id
[src]
impl Eq for Id
[src]
impl<'_> From<&'_ Id> for u64
[src]
impl From<Id> for u64
[src]
impl From<Id> for NonZeroU64
[src]
impl Hash for Id
[src]
fn hash<H: Hasher>(&self, state: &mut H)
[src]
fn hash_slice<H>(data: &[Self], state: &mut H) where
H: Hasher,
1.3.0[src]
H: Hasher,
impl Ord for Id
[src]
fn cmp(&self, o: &Self) -> Ordering
[src]
#[must_use]fn max(self, other: Self) -> Self
1.21.0[src]
#[must_use]fn min(self, other: Self) -> Self
1.21.0[src]
#[must_use]fn clamp(self, min: Self, max: Self) -> Self
[src]
impl PartialEq<Id> for Id
[src]
impl PartialEq<Id> for u64
[src]
impl PartialEq<u64> for Id
[src]
impl PartialOrd<Id> for Id
[src]
Auto Trait Implementations
Blanket Implementations
impl<T> Any for T where
T: 'static + ?Sized,
[src]
T: 'static + ?Sized,
impl<T> Borrow<T> for T where
T: ?Sized,
[src]
T: ?Sized,
impl<T> BorrowMut<T> for T where
T: ?Sized,
[src]
T: ?Sized,
pub fn borrow_mut(&mut self) -> &mut T
[src]
impl<T> From<T> for T
[src]
impl<T, U> Into<U> for T where
U: From<T>,
[src]
U: From<T>,
impl<T, U> TryFrom<U> for T where
U: Into<T>,
[src]
U: Into<T>,
type Error = Infallible
The type returned in the event of a conversion error.
pub fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
[src]
impl<T, U> TryInto<U> for T where
U: TryFrom<T>,
[src]
U: TryFrom<T>,