unique_token/lib.rs
1//! This crate provides a unique token type.
2
3#![forbid(unsafe_code)]
4
5use std::sync::atomic::{AtomicU64, Ordering};
6
7/// This type represents a unique token.
8///
9/// Each call to [`Unique::new()`] returns a unique value.
10/// The only way to create a token that compares equal is to
11/// clone or copy an existing token.
12///
13/// # Examples
14///
15/// ```
16/// use unique_token::Unique;
17///
18/// let x = Unique::new();
19/// let y = Unique::new();
20///
21/// // clones are equal
22/// assert_eq!(x, x.clone());
23/// assert_eq!(y, y.clone());
24///
25/// // tokens from different calls are unequal
26/// assert_ne!(x, y);
27/// ```
28///
29/// # Implementation
30///
31/// Each token is provided with a unique ID
32/// by incrementing a static [`AtomicU64`](std::sync::atomic::AtomicU64).
33#[derive(Clone, Copy, PartialEq, Eq, Hash)]
34pub struct Unique(u64);
35
36impl Unique {
37 /// Create a new token.
38 ///
39 /// All tokens created by this function compare unequal.
40 ///
41 /// # Panics
42 ///
43 /// This function panics if [`u64::MAX`]
44 /// unique tokens have been created.
45 /// In practice, this should never happen;
46 /// creating one token per nanosecond allows for
47 /// a runtime of almost six centuries.
48 #[inline]
49 pub fn new() -> Self {
50 static NEXT_ID: AtomicU64 = AtomicU64::new(1);
51
52 let id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
53 if id == 0 {
54 panic!("id overflow")
55 }
56 Self(id)
57 }
58}
59
60impl std::fmt::Debug for Unique {
61 fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
62 let len = (u64::BITS / 4) as usize;
63 write!(fmt, "0x{:0len$X}", u64::from(self))
64 }
65}
66
67impl From<&Unique> for u64 {
68 #[inline]
69 fn from(token: &Unique) -> u64 {
70 token.0
71 }
72}
73
74#[cfg(test)]
75mod tests {
76 use super::Unique;
77
78 #[test]
79 fn test_eq() {
80 let x = Unique::new();
81 let y = Unique::new();
82 assert_ne!(&x, &y);
83 assert_eq!(&x, &x.clone());
84 assert_eq!(&y, &y.clone());
85 }
86
87 #[test]
88 fn test_into_u64() {
89 let x = Unique::new();
90 let y = Unique::new();
91 assert_ne!(u64::from(&x), u64::from(&y));
92 assert_eq!(u64::from(&x), u64::from(&x.clone()));
93 assert_eq!(u64::from(&y), u64::from(&y.clone()));
94 }
95}