pub struct Snowflake { /* private fields */ }snowflake only.Expand description
Snowflake ID generator.
Holds the worker ID, the epoch, and the packed (last_ms, next_seq)
state used to mint monotonic IDs lock-free.
§Example
use id_forge::snowflake::Snowflake;
let gen = Snowflake::new(7);
let id = gen.next_id();
assert_eq!(Snowflake::parts(id).1, 7);Implementations§
Source§impl Snowflake
impl Snowflake
Sourcepub const fn new(worker_id: u16) -> Self
pub const fn new(worker_id: u16) -> Self
Build a new generator with the given worker ID (0-1023) and the default 2026-01-01 epoch.
Worker IDs above 1023 are silently clamped to 10 bits.
§Example
use id_forge::snowflake::Snowflake;
let gen = Snowflake::new(42);
assert_eq!(gen.worker_id(), 42);Examples found in repository?
33fn main() {
34 println!("id-forge throughput (single thread, release build)");
35 println!("---------------------------------------------------");
36
37 let iters = 1_000_000;
38 bench("Uuid::v4", iters, Uuid::v4);
39 bench("Uuid::v7", iters, Uuid::v7);
40 bench("Ulid::new", iters, Ulid::new);
41
42 let sf = Snowflake::new(1);
43 bench("Snowflake::next_id", iters, || sf.next_id());
44
45 bench("nanoid::generate", iters / 5, nanoid::generate);
46 bench("nanoid::with_length(8)", iters / 5, || {
47 nanoid::with_length(8)
48 });
49 bench("nanoid::custom(16, hex)", iters / 5, || {
50 nanoid::custom(16, b"0123456789abcdef")
51 });
52 bench("nanoid::custom(21, 17-char)", iters / 5, || {
53 nanoid::custom(21, b"ABCDEFGHIJKLMNOPQ")
54 });
55}More examples
7fn main() {
8 let v4 = Uuid::v4();
9 println!("UUID v4: {v4} (version={})", v4.version());
10 println!("UUID v7: {}", Uuid::v7());
11 println!("UUID nil: {}", Uuid::nil());
12
13 let a = Ulid::new();
14 let b = Ulid::new();
15 println!("ULID a: {a}");
16 println!("ULID b: {b} (monotonic: {})", b > a);
17
18 let gen = Snowflake::new(1);
19 let sf = gen.next_id();
20 let (ts_offset, worker, seq) = Snowflake::parts(sf);
21 println!(
22 "Snowflake: {sf} (ts+epoch={}, worker={worker}, seq={seq})",
23 ts_offset + gen.epoch_ms()
24 );
25
26 println!("NanoID 21: {}", nanoid::generate());
27 println!("NanoID 8: {}", nanoid::with_length(8));
28
29 assert_eq!(v4, Uuid::parse_str(&v4.to_string()).unwrap());
30 assert_eq!(a, Ulid::parse_str(&a.to_string()).unwrap());
31}21fn main() {
22 let gen = Snowflake::new(1);
23
24 println!("== Normal path ==");
25 match gen.try_next_id() {
26 Ok(id) => println!("ok: id = {id}"),
27 Err(e) => println!("err: {e}"),
28 }
29
30 println!("\n== Recovering from a transient skew ==");
31 // Real services will see this when an NTP correction nudges the
32 // clock backward by a millisecond or two. The recommended
33 // strategy is: short pause, then retry. After at most ~1ms the
34 // wall clock is ahead of `last_ms` again and try_next_id
35 // returns Ok.
36 let mut attempts = 0usize;
37 let id = loop {
38 attempts += 1;
39 match gen.try_next_id() {
40 Ok(id) => break id,
41 Err(ClockSkew { last_ms, now_ms }) => {
42 // Backoff proportional to how far we regressed.
43 let drift = last_ms.saturating_sub(now_ms);
44 eprintln!("skew detected, drift = {drift} ms; backing off");
45 std::thread::sleep(Duration::from_millis(drift.max(1) + 1));
46 if attempts > 16 {
47 panic!("clock failed to recover after {attempts} attempts");
48 }
49 }
50 }
51 };
52 println!("recovered = {id} after {attempts} attempt(s)");
53
54 println!("\n== Burst rate ==");
55 // The CAS state machine handles contention without locks. A
56 // single-thread tight loop here is the uncontended path.
57 let start = std::time::Instant::now();
58 let burst = 100_000;
59 for _ in 0..burst {
60 let _ = gen.next_id();
61 }
62 let elapsed = start.elapsed();
63 println!(
64 "{burst} IDs in {elapsed:?} ({:.0} ns/op)",
65 elapsed.as_nanos() as f64 / burst as f64
66 );
67}18fn main() {
19 println!("== Default epoch (2026-01-01) ==");
20 let gen = Snowflake::new(7);
21 let id = gen.next_id();
22 let (ts_offset, worker, seq) = Snowflake::parts(id);
23 let wall_ms = ts_offset + gen.epoch_ms();
24 println!("id = {id}");
25 println!("worker = {worker}");
26 println!("seq = {seq}");
27 println!("ts_offset_ms = {ts_offset}");
28 println!("wall_ms = {wall_ms}");
29 println!("default epoch = {DEFAULT_EPOCH_MS}");
30
31 println!("\n== Twitter's original 2010 epoch ==");
32 let twitter_epoch_ms = 1_288_834_974_657;
33 let tw = Snowflake::with_epoch(9, twitter_epoch_ms);
34 let tw_id = tw.next_id();
35 let (tw_ts, tw_worker, _) = Snowflake::parts(tw_id);
36 println!("id = {tw_id}");
37 println!("worker = {tw_worker}");
38 println!("ts_offset_ms = {tw_ts}");
39 println!("wall_ms = {}", tw_ts + tw.epoch_ms());
40
41 println!("\n== Multi-thread contention (8 threads x 2000 IDs) ==");
42 let gen = Arc::new(Snowflake::new(3));
43 let handles: Vec<_> = (0..8)
44 .map(|_| {
45 let g = Arc::clone(&gen);
46 thread::spawn(move || (0..2000).map(|_| g.next_id()).collect::<Vec<_>>())
47 })
48 .collect();
49
50 let mut all = HashSet::new();
51 for h in handles {
52 for id in h.join().unwrap() {
53 all.insert(id);
54 }
55 }
56 println!("expected = {}", 8 * 2000);
57 println!("unique seen = {}", all.len());
58 println!("no duplicates= {}", all.len() == 8 * 2000);
59
60 println!("\n== Worker ID clamping ==");
61 let clamped = Snowflake::new(0xFFFF);
62 println!(
63 "constructor input 0xFFFF -> worker_id() = {}",
64 clamped.worker_id()
65 );
66 println!("(10-bit max = {})", 0x3FF);
67}Sourcepub const fn with_epoch(worker_id: u16, epoch_ms: u64) -> Self
pub const fn with_epoch(worker_id: u16, epoch_ms: u64) -> Self
Build a new generator with a custom epoch (milliseconds since the UNIX epoch).
§Example
use id_forge::snowflake::Snowflake;
// Twitter's original Snowflake epoch.
let gen = Snowflake::with_epoch(1, 1_288_834_974_657);
assert_eq!(gen.epoch_ms(), 1_288_834_974_657);Examples found in repository?
18fn main() {
19 println!("== Default epoch (2026-01-01) ==");
20 let gen = Snowflake::new(7);
21 let id = gen.next_id();
22 let (ts_offset, worker, seq) = Snowflake::parts(id);
23 let wall_ms = ts_offset + gen.epoch_ms();
24 println!("id = {id}");
25 println!("worker = {worker}");
26 println!("seq = {seq}");
27 println!("ts_offset_ms = {ts_offset}");
28 println!("wall_ms = {wall_ms}");
29 println!("default epoch = {DEFAULT_EPOCH_MS}");
30
31 println!("\n== Twitter's original 2010 epoch ==");
32 let twitter_epoch_ms = 1_288_834_974_657;
33 let tw = Snowflake::with_epoch(9, twitter_epoch_ms);
34 let tw_id = tw.next_id();
35 let (tw_ts, tw_worker, _) = Snowflake::parts(tw_id);
36 println!("id = {tw_id}");
37 println!("worker = {tw_worker}");
38 println!("ts_offset_ms = {tw_ts}");
39 println!("wall_ms = {}", tw_ts + tw.epoch_ms());
40
41 println!("\n== Multi-thread contention (8 threads x 2000 IDs) ==");
42 let gen = Arc::new(Snowflake::new(3));
43 let handles: Vec<_> = (0..8)
44 .map(|_| {
45 let g = Arc::clone(&gen);
46 thread::spawn(move || (0..2000).map(|_| g.next_id()).collect::<Vec<_>>())
47 })
48 .collect();
49
50 let mut all = HashSet::new();
51 for h in handles {
52 for id in h.join().unwrap() {
53 all.insert(id);
54 }
55 }
56 println!("expected = {}", 8 * 2000);
57 println!("unique seen = {}", all.len());
58 println!("no duplicates= {}", all.len() == 8 * 2000);
59
60 println!("\n== Worker ID clamping ==");
61 let clamped = Snowflake::new(0xFFFF);
62 println!(
63 "constructor input 0xFFFF -> worker_id() = {}",
64 clamped.worker_id()
65 );
66 println!("(10-bit max = {})", 0x3FF);
67}Sourcepub const fn worker_id(&self) -> u16
pub const fn worker_id(&self) -> u16
The worker ID this generator was built with, clamped to 10 bits.
§Example
use id_forge::snowflake::Snowflake;
assert_eq!(Snowflake::new(42).worker_id(), 42);
assert_eq!(Snowflake::new(0xffff).worker_id(), 0x3ff); // clampedExamples found in repository?
18fn main() {
19 println!("== Default epoch (2026-01-01) ==");
20 let gen = Snowflake::new(7);
21 let id = gen.next_id();
22 let (ts_offset, worker, seq) = Snowflake::parts(id);
23 let wall_ms = ts_offset + gen.epoch_ms();
24 println!("id = {id}");
25 println!("worker = {worker}");
26 println!("seq = {seq}");
27 println!("ts_offset_ms = {ts_offset}");
28 println!("wall_ms = {wall_ms}");
29 println!("default epoch = {DEFAULT_EPOCH_MS}");
30
31 println!("\n== Twitter's original 2010 epoch ==");
32 let twitter_epoch_ms = 1_288_834_974_657;
33 let tw = Snowflake::with_epoch(9, twitter_epoch_ms);
34 let tw_id = tw.next_id();
35 let (tw_ts, tw_worker, _) = Snowflake::parts(tw_id);
36 println!("id = {tw_id}");
37 println!("worker = {tw_worker}");
38 println!("ts_offset_ms = {tw_ts}");
39 println!("wall_ms = {}", tw_ts + tw.epoch_ms());
40
41 println!("\n== Multi-thread contention (8 threads x 2000 IDs) ==");
42 let gen = Arc::new(Snowflake::new(3));
43 let handles: Vec<_> = (0..8)
44 .map(|_| {
45 let g = Arc::clone(&gen);
46 thread::spawn(move || (0..2000).map(|_| g.next_id()).collect::<Vec<_>>())
47 })
48 .collect();
49
50 let mut all = HashSet::new();
51 for h in handles {
52 for id in h.join().unwrap() {
53 all.insert(id);
54 }
55 }
56 println!("expected = {}", 8 * 2000);
57 println!("unique seen = {}", all.len());
58 println!("no duplicates= {}", all.len() == 8 * 2000);
59
60 println!("\n== Worker ID clamping ==");
61 let clamped = Snowflake::new(0xFFFF);
62 println!(
63 "constructor input 0xFFFF -> worker_id() = {}",
64 clamped.worker_id()
65 );
66 println!("(10-bit max = {})", 0x3FF);
67}Sourcepub const fn epoch_ms(&self) -> u64
pub const fn epoch_ms(&self) -> u64
The epoch this generator subtracts from the wall clock.
§Example
use id_forge::snowflake::{Snowflake, DEFAULT_EPOCH_MS};
assert_eq!(Snowflake::new(1).epoch_ms(), DEFAULT_EPOCH_MS);Examples found in repository?
7fn main() {
8 let v4 = Uuid::v4();
9 println!("UUID v4: {v4} (version={})", v4.version());
10 println!("UUID v7: {}", Uuid::v7());
11 println!("UUID nil: {}", Uuid::nil());
12
13 let a = Ulid::new();
14 let b = Ulid::new();
15 println!("ULID a: {a}");
16 println!("ULID b: {b} (monotonic: {})", b > a);
17
18 let gen = Snowflake::new(1);
19 let sf = gen.next_id();
20 let (ts_offset, worker, seq) = Snowflake::parts(sf);
21 println!(
22 "Snowflake: {sf} (ts+epoch={}, worker={worker}, seq={seq})",
23 ts_offset + gen.epoch_ms()
24 );
25
26 println!("NanoID 21: {}", nanoid::generate());
27 println!("NanoID 8: {}", nanoid::with_length(8));
28
29 assert_eq!(v4, Uuid::parse_str(&v4.to_string()).unwrap());
30 assert_eq!(a, Ulid::parse_str(&a.to_string()).unwrap());
31}More examples
18fn main() {
19 println!("== Default epoch (2026-01-01) ==");
20 let gen = Snowflake::new(7);
21 let id = gen.next_id();
22 let (ts_offset, worker, seq) = Snowflake::parts(id);
23 let wall_ms = ts_offset + gen.epoch_ms();
24 println!("id = {id}");
25 println!("worker = {worker}");
26 println!("seq = {seq}");
27 println!("ts_offset_ms = {ts_offset}");
28 println!("wall_ms = {wall_ms}");
29 println!("default epoch = {DEFAULT_EPOCH_MS}");
30
31 println!("\n== Twitter's original 2010 epoch ==");
32 let twitter_epoch_ms = 1_288_834_974_657;
33 let tw = Snowflake::with_epoch(9, twitter_epoch_ms);
34 let tw_id = tw.next_id();
35 let (tw_ts, tw_worker, _) = Snowflake::parts(tw_id);
36 println!("id = {tw_id}");
37 println!("worker = {tw_worker}");
38 println!("ts_offset_ms = {tw_ts}");
39 println!("wall_ms = {}", tw_ts + tw.epoch_ms());
40
41 println!("\n== Multi-thread contention (8 threads x 2000 IDs) ==");
42 let gen = Arc::new(Snowflake::new(3));
43 let handles: Vec<_> = (0..8)
44 .map(|_| {
45 let g = Arc::clone(&gen);
46 thread::spawn(move || (0..2000).map(|_| g.next_id()).collect::<Vec<_>>())
47 })
48 .collect();
49
50 let mut all = HashSet::new();
51 for h in handles {
52 for id in h.join().unwrap() {
53 all.insert(id);
54 }
55 }
56 println!("expected = {}", 8 * 2000);
57 println!("unique seen = {}", all.len());
58 println!("no duplicates= {}", all.len() == 8 * 2000);
59
60 println!("\n== Worker ID clamping ==");
61 let clamped = Snowflake::new(0xFFFF);
62 println!(
63 "constructor input 0xFFFF -> worker_id() = {}",
64 clamped.worker_id()
65 );
66 println!("(10-bit max = {})", 0x3FF);
67}Sourcepub fn try_next_id(&self) -> Result<u64, ClockSkew>
pub fn try_next_id(&self) -> Result<u64, ClockSkew>
Generate the next ID, returning Err if the wall clock has
moved backward since the previous call.
When 4096 IDs have been issued in the same millisecond, this method blocks in a microsecond sleep loop until the wall clock advances. It does not spin on a busy loop.
§Example
use id_forge::snowflake::Snowflake;
let gen = Snowflake::new(1);
let id = gen.try_next_id().expect("clock should not run backward");
assert!(id > 0);Examples found in repository?
21fn main() {
22 let gen = Snowflake::new(1);
23
24 println!("== Normal path ==");
25 match gen.try_next_id() {
26 Ok(id) => println!("ok: id = {id}"),
27 Err(e) => println!("err: {e}"),
28 }
29
30 println!("\n== Recovering from a transient skew ==");
31 // Real services will see this when an NTP correction nudges the
32 // clock backward by a millisecond or two. The recommended
33 // strategy is: short pause, then retry. After at most ~1ms the
34 // wall clock is ahead of `last_ms` again and try_next_id
35 // returns Ok.
36 let mut attempts = 0usize;
37 let id = loop {
38 attempts += 1;
39 match gen.try_next_id() {
40 Ok(id) => break id,
41 Err(ClockSkew { last_ms, now_ms }) => {
42 // Backoff proportional to how far we regressed.
43 let drift = last_ms.saturating_sub(now_ms);
44 eprintln!("skew detected, drift = {drift} ms; backing off");
45 std::thread::sleep(Duration::from_millis(drift.max(1) + 1));
46 if attempts > 16 {
47 panic!("clock failed to recover after {attempts} attempts");
48 }
49 }
50 }
51 };
52 println!("recovered = {id} after {attempts} attempt(s)");
53
54 println!("\n== Burst rate ==");
55 // The CAS state machine handles contention without locks. A
56 // single-thread tight loop here is the uncontended path.
57 let start = std::time::Instant::now();
58 let burst = 100_000;
59 for _ in 0..burst {
60 let _ = gen.next_id();
61 }
62 let elapsed = start.elapsed();
63 println!(
64 "{burst} IDs in {elapsed:?} ({:.0} ns/op)",
65 elapsed.as_nanos() as f64 / burst as f64
66 );
67}Sourcepub fn next_id(&self) -> u64
pub fn next_id(&self) -> u64
Generate the next ID. Panics if the wall clock has moved backward since the previous call.
Use Snowflake::try_next_id when callers need to recover
from clock skew (e.g. to surface it as a service-level error
rather than crash the process).
§Panics
Panics with a ClockSkew description if the wall clock has
regressed below the most recently issued ID’s timestamp.
§Example
use id_forge::snowflake::Snowflake;
let gen = Snowflake::new(1);
let id = gen.next_id();
assert!(id > 0);Examples found in repository?
33fn main() {
34 println!("id-forge throughput (single thread, release build)");
35 println!("---------------------------------------------------");
36
37 let iters = 1_000_000;
38 bench("Uuid::v4", iters, Uuid::v4);
39 bench("Uuid::v7", iters, Uuid::v7);
40 bench("Ulid::new", iters, Ulid::new);
41
42 let sf = Snowflake::new(1);
43 bench("Snowflake::next_id", iters, || sf.next_id());
44
45 bench("nanoid::generate", iters / 5, nanoid::generate);
46 bench("nanoid::with_length(8)", iters / 5, || {
47 nanoid::with_length(8)
48 });
49 bench("nanoid::custom(16, hex)", iters / 5, || {
50 nanoid::custom(16, b"0123456789abcdef")
51 });
52 bench("nanoid::custom(21, 17-char)", iters / 5, || {
53 nanoid::custom(21, b"ABCDEFGHIJKLMNOPQ")
54 });
55}More examples
7fn main() {
8 let v4 = Uuid::v4();
9 println!("UUID v4: {v4} (version={})", v4.version());
10 println!("UUID v7: {}", Uuid::v7());
11 println!("UUID nil: {}", Uuid::nil());
12
13 let a = Ulid::new();
14 let b = Ulid::new();
15 println!("ULID a: {a}");
16 println!("ULID b: {b} (monotonic: {})", b > a);
17
18 let gen = Snowflake::new(1);
19 let sf = gen.next_id();
20 let (ts_offset, worker, seq) = Snowflake::parts(sf);
21 println!(
22 "Snowflake: {sf} (ts+epoch={}, worker={worker}, seq={seq})",
23 ts_offset + gen.epoch_ms()
24 );
25
26 println!("NanoID 21: {}", nanoid::generate());
27 println!("NanoID 8: {}", nanoid::with_length(8));
28
29 assert_eq!(v4, Uuid::parse_str(&v4.to_string()).unwrap());
30 assert_eq!(a, Ulid::parse_str(&a.to_string()).unwrap());
31}21fn main() {
22 let gen = Snowflake::new(1);
23
24 println!("== Normal path ==");
25 match gen.try_next_id() {
26 Ok(id) => println!("ok: id = {id}"),
27 Err(e) => println!("err: {e}"),
28 }
29
30 println!("\n== Recovering from a transient skew ==");
31 // Real services will see this when an NTP correction nudges the
32 // clock backward by a millisecond or two. The recommended
33 // strategy is: short pause, then retry. After at most ~1ms the
34 // wall clock is ahead of `last_ms` again and try_next_id
35 // returns Ok.
36 let mut attempts = 0usize;
37 let id = loop {
38 attempts += 1;
39 match gen.try_next_id() {
40 Ok(id) => break id,
41 Err(ClockSkew { last_ms, now_ms }) => {
42 // Backoff proportional to how far we regressed.
43 let drift = last_ms.saturating_sub(now_ms);
44 eprintln!("skew detected, drift = {drift} ms; backing off");
45 std::thread::sleep(Duration::from_millis(drift.max(1) + 1));
46 if attempts > 16 {
47 panic!("clock failed to recover after {attempts} attempts");
48 }
49 }
50 }
51 };
52 println!("recovered = {id} after {attempts} attempt(s)");
53
54 println!("\n== Burst rate ==");
55 // The CAS state machine handles contention without locks. A
56 // single-thread tight loop here is the uncontended path.
57 let start = std::time::Instant::now();
58 let burst = 100_000;
59 for _ in 0..burst {
60 let _ = gen.next_id();
61 }
62 let elapsed = start.elapsed();
63 println!(
64 "{burst} IDs in {elapsed:?} ({:.0} ns/op)",
65 elapsed.as_nanos() as f64 / burst as f64
66 );
67}18fn main() {
19 println!("== Default epoch (2026-01-01) ==");
20 let gen = Snowflake::new(7);
21 let id = gen.next_id();
22 let (ts_offset, worker, seq) = Snowflake::parts(id);
23 let wall_ms = ts_offset + gen.epoch_ms();
24 println!("id = {id}");
25 println!("worker = {worker}");
26 println!("seq = {seq}");
27 println!("ts_offset_ms = {ts_offset}");
28 println!("wall_ms = {wall_ms}");
29 println!("default epoch = {DEFAULT_EPOCH_MS}");
30
31 println!("\n== Twitter's original 2010 epoch ==");
32 let twitter_epoch_ms = 1_288_834_974_657;
33 let tw = Snowflake::with_epoch(9, twitter_epoch_ms);
34 let tw_id = tw.next_id();
35 let (tw_ts, tw_worker, _) = Snowflake::parts(tw_id);
36 println!("id = {tw_id}");
37 println!("worker = {tw_worker}");
38 println!("ts_offset_ms = {tw_ts}");
39 println!("wall_ms = {}", tw_ts + tw.epoch_ms());
40
41 println!("\n== Multi-thread contention (8 threads x 2000 IDs) ==");
42 let gen = Arc::new(Snowflake::new(3));
43 let handles: Vec<_> = (0..8)
44 .map(|_| {
45 let g = Arc::clone(&gen);
46 thread::spawn(move || (0..2000).map(|_| g.next_id()).collect::<Vec<_>>())
47 })
48 .collect();
49
50 let mut all = HashSet::new();
51 for h in handles {
52 for id in h.join().unwrap() {
53 all.insert(id);
54 }
55 }
56 println!("expected = {}", 8 * 2000);
57 println!("unique seen = {}", all.len());
58 println!("no duplicates= {}", all.len() == 8 * 2000);
59
60 println!("\n== Worker ID clamping ==");
61 let clamped = Snowflake::new(0xFFFF);
62 println!(
63 "constructor input 0xFFFF -> worker_id() = {}",
64 clamped.worker_id()
65 );
66 println!("(10-bit max = {})", 0x3FF);
67}Sourcepub const fn parts(id: u64) -> (u64, u16, u16)
pub const fn parts(id: u64) -> (u64, u16, u16)
Decompose an ID minted by any Snowflake generator into its
(timestamp_offset_ms, worker_id, sequence) parts.
The first element is the millisecond offset from whatever epoch
the originating generator was built with. To recover the
wall-clock millisecond, add the generator’s epoch_ms.
§Example
use id_forge::snowflake::Snowflake;
let gen = Snowflake::new(7);
let id = gen.next_id();
let (ts_offset, worker, seq) = Snowflake::parts(id);
assert_eq!(worker, 7);
assert!(seq <= 4095);
let wall_ms = ts_offset + gen.epoch_ms();
assert!(wall_ms > gen.epoch_ms());Examples found in repository?
7fn main() {
8 let v4 = Uuid::v4();
9 println!("UUID v4: {v4} (version={})", v4.version());
10 println!("UUID v7: {}", Uuid::v7());
11 println!("UUID nil: {}", Uuid::nil());
12
13 let a = Ulid::new();
14 let b = Ulid::new();
15 println!("ULID a: {a}");
16 println!("ULID b: {b} (monotonic: {})", b > a);
17
18 let gen = Snowflake::new(1);
19 let sf = gen.next_id();
20 let (ts_offset, worker, seq) = Snowflake::parts(sf);
21 println!(
22 "Snowflake: {sf} (ts+epoch={}, worker={worker}, seq={seq})",
23 ts_offset + gen.epoch_ms()
24 );
25
26 println!("NanoID 21: {}", nanoid::generate());
27 println!("NanoID 8: {}", nanoid::with_length(8));
28
29 assert_eq!(v4, Uuid::parse_str(&v4.to_string()).unwrap());
30 assert_eq!(a, Ulid::parse_str(&a.to_string()).unwrap());
31}More examples
18fn main() {
19 println!("== Default epoch (2026-01-01) ==");
20 let gen = Snowflake::new(7);
21 let id = gen.next_id();
22 let (ts_offset, worker, seq) = Snowflake::parts(id);
23 let wall_ms = ts_offset + gen.epoch_ms();
24 println!("id = {id}");
25 println!("worker = {worker}");
26 println!("seq = {seq}");
27 println!("ts_offset_ms = {ts_offset}");
28 println!("wall_ms = {wall_ms}");
29 println!("default epoch = {DEFAULT_EPOCH_MS}");
30
31 println!("\n== Twitter's original 2010 epoch ==");
32 let twitter_epoch_ms = 1_288_834_974_657;
33 let tw = Snowflake::with_epoch(9, twitter_epoch_ms);
34 let tw_id = tw.next_id();
35 let (tw_ts, tw_worker, _) = Snowflake::parts(tw_id);
36 println!("id = {tw_id}");
37 println!("worker = {tw_worker}");
38 println!("ts_offset_ms = {tw_ts}");
39 println!("wall_ms = {}", tw_ts + tw.epoch_ms());
40
41 println!("\n== Multi-thread contention (8 threads x 2000 IDs) ==");
42 let gen = Arc::new(Snowflake::new(3));
43 let handles: Vec<_> = (0..8)
44 .map(|_| {
45 let g = Arc::clone(&gen);
46 thread::spawn(move || (0..2000).map(|_| g.next_id()).collect::<Vec<_>>())
47 })
48 .collect();
49
50 let mut all = HashSet::new();
51 for h in handles {
52 for id in h.join().unwrap() {
53 all.insert(id);
54 }
55 }
56 println!("expected = {}", 8 * 2000);
57 println!("unique seen = {}", all.len());
58 println!("no duplicates= {}", all.len() == 8 * 2000);
59
60 println!("\n== Worker ID clamping ==");
61 let clamped = Snowflake::new(0xFFFF);
62 println!(
63 "constructor input 0xFFFF -> worker_id() = {}",
64 clamped.worker_id()
65 );
66 println!("(10-bit max = {})", 0x3FF);
67}