uuid
stdlib/extra/uuid.hk: RFC 9562 UUIDs, v4 for identity and v7 for order.
A Uuid is sixteen bytes, opaque so the version and variant bits cannot be broken by arithmetic on them. It renders and parses in the canonical 8-4-4-4-12 lowercase form alone, which is the form every wire protocol and database column agrees on.
Two versions, answering different questions:
new_v4! 122 random bits. The identity is unrelated to when it was made, which is what it is for: nothing leaks and nothing collides. new_v7! a 48-bit Unix-millisecond prefix and a random tail. Sorts by creation time as a string, as bytes, and as a database key, so a primary key remains cache-friendly in place of scattering the index the way v4 does.
Parsing and rendering are pure; only generation is effectful. new_v4! charges [random] and new_v7! [random, time], and a program's capability surface therefore says which one it reached for.
Under --deterministic the random draw comes from the seeded stream, and a run that generates UUIDs replays them: the same seed gives the same identifiers, which is what makes a test that stores one and asserts on it reproducible and no snapshot that changes every run. new_v7! also reads the gate's virtual clock, and its prefix replays too.
Uuid
opaque Uuid
raw: bytes
end
A UUID: sixteen bytes with the version and variant bits set per RFC 9562.
Opaque because the bit layout is a contract with every other implementation in the world. Construct one with new_v4!/new_v7! or Uuid.parse, read it back with to_string or to_bytes.
impl Uuid
nil
def nil() -> Uuid
The nil UUID, all sixteen bytes zero: RFC 9562's explicit "no value".
It is the one UUID with no version - version answers 0u8 - so it is distinguishable from any generated one and never merely unlikely.
Uuid.nil().to_string() => "00000000-0000-0000-0000-000000000000"
Uuid.nil().version => 0u8
to_bytes
def to_bytes(self) -> bytes
The sixteen bytes, big-endian, as they travel on a wire or into a BINARY(16) column.
Uuid.nil().to_bytes().length => 16
version
prop version(self) -> u8
The version nibble: 4u8 for new_v4!, 7u8 for new_v7!, 0u8 for the nil UUID.
Uuid.nil().version => 0u8
impl Display<Uuid>
to_string
def to_string(self) -> string
The canonical form: 32 lowercase hex digits in 8-4-4-4-12 groups.
Lowercase because RFC 9562 requires generated output to be lowercase, even though parsing accepts either case.
Uuid.nil().to_string() => "00000000-0000-0000-0000-000000000000"
impl FromString<Uuid>
parse
def parse(s: string) -> Result<Uuid, ParseError>
Read a UUID in canonical 8-4-4-4-12 form. Case-insensitive, since the same value arrives upper-cased from plenty of systems, but the dashes are required and so is the length: a bare 32-digit string is a different format, and accepting it here would make this parser the one that decides what a UUID looks like.
Uuid.parse("00000000-0000-0000-0000-000000000000").map(|u| u.version).unwrap_or(9u8) => 0u8
Uuid.parse("not-a-uuid").map(|u| u.version).unwrap_or(9u8) => 9u8
impl Eq<Uuid>
eq?
def eq?(self, other: Self) -> bool
Two UUIDs are equal when all sixteen bytes are.
(Uuid.nil() == Uuid.nil()) => true
impl Ord<Uuid>
cmp
def cmp(self, other: Self) -> Ordering
Byte order, which for a v7 UUID is creation order: the timestamp is the big-endian prefix, and sorting the identifiers sorts the rows.
Uuid.nil().cmp(Uuid.nil()) => Equal
impl Hash<Uuid>
hash
prop hash(self) -> u64
Hashes the bytes, which lets a Uuid work as a Map key.
(Uuid.nil().hash == Uuid.nil().hash) => true
new_v4!
def new_v4!() -> Uuid [random]
A version 4 UUID: 122 random bits, with the six version and variant bits fixed. Nothing about it says when or where it was made.
Charges [random] alone: no clock is read, which is the difference from new_v7! in both capability and behaviour. @no-doctest: draws random bytes; no fixed value to assert
new_v7!
def new_v7!() -> Uuid [random, time]
A version 7 UUID: a 48-bit Unix-millisecond prefix and 74 random bits, with the same six bits fixed. Two UUIDs made in different milliseconds sort in the order they were made, as text and as bytes alike.
Charges [random, time]: the clock read is what produces the ordering, and a signature that hides it would be lying about the dependency. @no-doctest: reads the clock and draws random bytes; no fixed value to assert
_stamp
def _stamp(b: bytes, version: u8) -> bytes
Set the version nibble (high half of byte 6) and the variant bits (high two bits of byte 8 to 10), leaving the other 122 bits alone. Every UUID this module produces goes through here, which asserts the RFC 9562 layout in one place and never at each constructor.
_be48
def _be48(ms: i64) -> bytes
The low 48 bits of ms, big-endian, which is the v7 prefix. Big-endian is what makes the byte order the time order.
_zeros
def _zeros() -> bytes
Sixteen zero bytes.
_refused?
def _refused?(s: string) -> bool
Did parse reject s? Result has no emptiness predicate, and the refusals are therefore asserted through one match and never six.
_dashed?
def _dashed?(s: string) -> bool
Are the four dashes where the canonical form puts them? The caller has already checked the length.
_ones
def _ones() -> bytes