Loading page…
Rust walkthroughs
Loading page…
The uuid crate provides generation and parsing of Universally Unique Identifiers (UUIDs). UUIDs are 128-bit identifiers used for unique keys, database primary keys, tracking identifiers, and distributed systems. The crate supports all UUID versions including v1 (timestamp/MAC), v3 (MD5 hash), v4 (random), v5 (SHA-1 hash), and v7 (time-ordered). Version 4 (random) is the most commonly used for general purposes.
Key concepts:
v1 feature)v3 feature)v4 feature)v5 feature)v7 feature)# Cargo.toml
[dependencies]
uuid = { version = "1", features = ["v4", "serde"] }use uuid::Uuid;
fn main() {
// Generate a random UUID (v4)
let id = Uuid::new_v4();
println!("UUID: {}", id);
// Parse a UUID string
let parsed = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
println!("Parsed: {}", parsed);
// Check version
println!("Version: {:?}", parsed.get_version());
}use uuid::Uuid;
fn main() {
// v4 - Random UUID (most common)
let id1 = Uuid::new_v4();
println!("v4: {}", id1);
let id2 = Uuid::new_v4();
println!("Another v4: {}", id2);
// Each call generates a different UUID
println!("Equal: {}", id1 == id2);
// nil UUID (all zeros)
let nil = Uuid::nil();
println!("nil: {}", nil);
// max UUID (all ones)
let max = Uuid::max();
println!("max: {}", max);
}
// With v1 feature enabled in Cargo.toml:
// [dependencies]
// uuid = { version = "1", features = ["v1"] }
fn v1_example() {
use uuid::Uuid;
// v1 requires a timestamp and node ID
// This is typically done with a context provider
// v1 UUIDs are less common due to privacy concerns with MAC addresses
}
// With v3 feature enabled:
// [dependencies]
// uuid = { version = "1", features = ["v3"] }
fn v3_example() {
use uuid::Uuid;
// v3 - MD5 hash based
// Same namespace + name always produces same UUID
let namespace = Uuid::NAMESPACE_DNS;
let name = "example.com";
let id = Uuid::new_v3(namespace, name);
println!("v3: {}", id);
// Same inputs = same UUID
let id2 = Uuid::new_v3(namespace, name);
assert_eq!(id, id2);
}
// With v5 feature enabled:
// [dependencies]
// uuid = { version = "1", features = ["v5"] }
fn v5_example() {
use uuid::Uuid;
// v5 - SHA-1 hash based (preferred over v3)
let namespace = Uuid::NAMESPACE_DNS;
let name = "example.com";
let id = Uuid::new_v5(namespace, name);
println!("v5: {}", id);
// Deterministic - same inputs always produce same UUID
let id2 = Uuid::new_v5(namespace, name);
assert_eq!(id, id2);
// Different name = different UUID
let other = Uuid::new_v5(namespace, "different.com");
assert_ne!(id, other);
}
// With v7 feature enabled:
// [dependencies]
// uuid = { version = "1", features = ["v7"] }
fn v7_example() {
use uuid::Uuid;
// v7 - Time-ordered UUID (newer standard)
// Combines timestamp with random bits
let id = Uuid::now_v7();
println!("v7: {}", id);
// v7 UUIDs are sortable by time
let id1 = Uuid::now_v7();
std::thread::sleep(std::time::Duration::from_millis(1));
let id2 = Uuid::now_v7();
// id2 was created after id1
println!("id1: {}", id1);
println!("id2: {}", id2);
}use uuid::Uuid;
fn main() {
// Parse from standard hyphenated format
let id = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
println!("Parsed: {}", id);
// Parse from simple format (no hyphens)
let simple = Uuid::parse_str("550e8400e29b41d4a716446655440000").unwrap();
println!("Simple: {}", simple);
// Both represent the same UUID
assert_eq!(id, simple);
// Parse with braces
let braced = Uuid::parse_str("{550e8400-e29b-41d4-a716-446655440000}").unwrap();
println!("Braced: {}", braced);
// Parse URN format
let urn = Uuid::parse_str("urn:uuid:550e8400-e29b-41d4-a716-446655440000").unwrap();
println!("URN: {}", urn);
// Invalid UUID
let result = Uuid::parse_str("not-a-uuid");
println!("Invalid: {:?}", result);
// Parse with error handling
match Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000") {
Ok(uuid) => println!("Valid: {}", uuid),
Err(e) => println!("Error: {}", e),
}
}
// TryParse trait for cleaner error handling
fn try_parse_example() {
use uuid::Uuid;
use std::str::FromStr;
// Using FromStr trait
let id: Uuid = "550e8400-e29b-41d4-a716-446655440000".parse().unwrap();
println!("FromStr: {}", id);
// Or explicitly
let id = Uuid::from_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
println!("from_str: {}", id);
}use uuid::Uuid;
fn main() {
let id = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
// Default Display (hyphenated)
println!("Display: {}", id);
// Hyphenated format
println!("Hyphenated: {}", id.hyphenated());
// Simple format (no hyphens)
println!("Simple: {}", id.simple());
// URN format
println!("URN: {}", id.urn());
// Braced format
println!("Braced: {}", id.braced());
// Get bytes
let bytes = id.as_bytes();
println!("Bytes: {:?}", bytes);
println!("Byte length: {}", bytes.len());
// Convert to various formats
let hyphenated: String = id.hyphenated().to_string();
let simple: String = id.simple().to_string();
let urn: String = id.urn().to_string();
println!("Strings: {} | {} | {}", hyphenated, simple, urn);
// Upper case
println!("Upper: {}", id.simple().to_string().to_uppercase());
}
// Encoding examples
fn encoding_example() {
use uuid::Uuid;
let id = Uuid::new_v4();
// Get as byte array
let bytes: &[u8; 16] = id.as_bytes();
println!("Bytes: {:02x?}", bytes);
// Get as fields (time_low, time_mid, time_hi_and_version, etc.)
let (time_low, time_mid, time_hi_and_version, clock_seq_and_node) = id.as_fields();
println!("Fields: {:08x}-{:04x}-{:04x}-...", time_low, time_mid, time_hi_and_version);
// Convert to u128
let num: u128 = id.as_u128();
println!("u128: {}", num);
// Convert to [u8; 16]
let arr: [u8; 16] = *id.as_bytes();
println!("Array: {:02x?}", arr);
}use uuid::Uuid;
fn main() {
// From bytes
let bytes: [u8; 16] = [
0x55, 0x0e, 0x84, 0x00,
0xe2, 0x9b,
0x41, 0xd4,
0xa7, 0x16,
0x44, 0x66, 0x55, 0x44, 0x00, 0x00,
];
let id = Uuid::from_bytes(bytes);
println!("From bytes: {}", id);
// From bytes ref
let id = Uuid::from_bytes_ref(&bytes);
println!("From bytes ref: {}", id);
// From u128
let num: u128 = 0x550e8400_e29b_41d4_a716_446655440000;
let id = Uuid::from_u128(num);
println!("From u128: {}", id);
// From fields
let id = Uuid::from_fields(
0x550e8400, // time_low
0xe29b, // time_mid
0x41d4, // time_hi_and_version
&[0xa7, 0x16, 0x44, 0x66, 0x55, 0x44, 0x00, 0x00], // clock_seq_and_node
);
println!("From fields: {}", id);
// From u64 pair
let id = Uuid::from_u64_pair(0x550e8400e29b41d4, 0xa716446655440000);
println!("From u64 pair: {}", id);
}use uuid::Uuid;
fn main() {
let id = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
// Check if nil (all zeros)
println!("Is nil: {}", id.is_nil());
// Check version
println!("Version: {:?}", id.get_version());
// Check version number
if let Some(version) = id.get_version() {
println!("Version: {:?}", version);
}
// Check variant
println!("Variant: {:?}", id.get_variant());
// Get version number directly
let version_num = id.get_version_num();
println!("Version number: {}", version_num);
// Check if valid (proper version/variant)
println!("Valid: {}", id.get_version().is_some());
// Nil UUID
let nil = Uuid::nil();
println!("nil is nil: {}", nil.is_nil());
// Max UUID
let max = Uuid::max();
println!("max: {}", max);
}
// Version info
fn version_info() {
use uuid::{Uuid, Version};
let v4 = Uuid::new_v4();
println!("v4 version: {:?}", v4.get_version());
// Parse known versions
let v1_str = "d9428888-122b-11e1-b85c-61cd3cbb3210"; // v1
let v4_str = "d9428888-122b-11e1-b85c-61cd3cbb3210".replace("11e1", "41e1");
let v1 = Uuid::parse_str(v1_str).unwrap();
println!("v1 version: {:?}", v1.get_version());
}use uuid::Uuid;
use std::collections::{HashMap, HashSet, BTreeSet};
fn main() {
let id1 = Uuid::new_v4();
let id2 = Uuid::new_v4();
let id3 = Uuid::new_v4();
// Equality
println!("id1 == id1: {}", id1 == id1);
println!("id1 == id2: {}", id1 == id2);
// Ordering
println!("id1 < id2: {}", id1 < id2);
println!("id1 < id3: {}", id1 < id3);
// Hash - use in HashMap/HashSet
let mut set = HashSet::new();
set.insert(id1);
set.insert(id2);
set.insert(id1); // Duplicate, won't be added
println!("Set size: {}", set.len()); // 2
// HashMap
let mut map = HashMap::new();
map.insert(id1, "first");
map.insert(id2, "second");
println!("map[id1]: {:?}", map.get(&id1));
// BTreeSet - sorted order
let mut sorted = BTreeSet::new();
sorted.insert(id3);
sorted.insert(id1);
sorted.insert(id2);
println!("Sorted:");
for id in &sorted {
println!(" {}", id);
}
}
// v7 UUIDs maintain time order
fn v7_sorting() {
// Requires v7 feature
// let id1 = Uuid::now_v7();
// std::thread::sleep(std::time::Duration::from_millis(1));
// let id2 = Uuid::now_v7();
// std::thread::sleep(std::time::Duration::from_millis(1));
// let id3 = Uuid::now_v7();
// v7 UUIDs sort chronologically
// assert!(id1 < id2);
// assert!(id2 < id3);
}# Cargo.toml
[dependencies]
uuid = { version = "1", features = ["v4", "serde"] }
serde = { version = "1", features = ["derive"] }use uuid::Uuid;
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
struct User {
id: Uuid,
name: String,
email: String,
}
fn main() {
let user = User {
id: Uuid::new_v4(),
name: "Alice".to_string(),
email: "alice@example.com".to_string(),
};
// Serialize to JSON
let json = serde_json::to_string(&user).unwrap();
println!("JSON: {}", json);
// Deserialize from JSON
let parsed: User = serde_json::from_str(&json).unwrap();
println!("Parsed: {:?}", parsed);
// UUIDs serialize as strings by default
// {"id":"550e8400-e29b-41d4-a716-446655440000","name":"Alice","email":"alice@example.com"}
}
// Different serialization formats
fn serialization_formats() {
use uuid::Uuid;
use serde_json;
let id = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
// Default (hyphenated string)
let json = serde_json::to_string(&id).unwrap();
println!("Default: {}", json); // "550e8400-..."
}use uuid::Uuid;
fn main() {
// Pre-defined namespaces for v3/v5
// DNS namespace
let dns_ns = Uuid::NAMESPACE_DNS;
println!("DNS namespace: {}", dns_ns);
// URL namespace
let url_ns = Uuid::NAMESPACE_URL;
println!("URL namespace: {}", url_ns);
// OID namespace (ISO OID)
let oid_ns = Uuid::NAMESPACE_OID;
println!("OID namespace: {}", oid_ns);
// X.500 DN namespace
let x500_ns = Uuid::NAMESPACE_X500;
println!("X500 namespace: {}", x500_ns);
}
// Creating deterministic UUIDs with namespaces
// Requires v5 feature
fn deterministic_uuids() {
use uuid::Uuid;
let namespace = Uuid::NAMESPACE_DNS;
// Same namespace + name = same UUID
let id1 = Uuid::new_v5(namespace, "user@example.com");
let id2 = Uuid::new_v5(namespace, "user@example.com");
assert_eq!(id1, id2);
let id3 = Uuid::new_v5(namespace, "different@example.com");
assert_ne!(id1, id3);
// Useful for generating consistent IDs from email addresses, URLs, etc.
println!("Email UUID: {}", id1);
println!("Different email UUID: {}", id3);
// URL namespace
let url_ns = Uuid::NAMESPACE_URL;
let url_id = Uuid::new_v5(url_ns, "https://example.com/page");
println!("URL UUID: {}", url_id);
}use uuid::{Uuid, Builder};
fn main() {
// Build UUID from components
let id = Builder::from_bytes([
0x55, 0x0e, 0x84, 0x00,
0xe2, 0x9b,
0x41, 0xd4,
0xa7, 0x16,
0x44, 0x66, 0x55, 0x44, 0x00, 0x00,
])
.build();
println!("Built: {}", id);
// Set version and variant
let id = Builder::from_bytes([0; 16])
.set_version(uuid::Version::Random)
.set_variant(uuid::Variant::RFC4122)
.build();
println!("With version/variant: {}", id);
}use uuid::Uuid;
fn main() {
let id = Uuid::new_v4();
// To String
let s = id.to_string();
println!("String: {}", s);
// To hyphenated string (same as Display)
let hyphenated = id.hyphenated().to_string();
println!("Hyphenated: {}", hyphenated);
// To simple string (no hyphens)
let simple = id.simple().to_string();
println!("Simple: {}", simple);
// To URN
let urn = id.urn().to_string();
println!("URN: {}", urn);
// To bytes
let bytes: &[u8; 16] = id.as_bytes();
println!("Bytes: {:02x?}", bytes);
// To u128
let num: u128 = id.as_u128();
println!("u128: {}", num);
// To hex string
let hex = hex::encode(id.as_bytes());
println!("Hex: {}", hex);
// From various formats back to UUID
let from_str: Uuid = s.parse().unwrap();
let from_simple = Uuid::parse_str(&simple).unwrap();
let from_bytes = Uuid::from_bytes(*bytes);
let from_u128 = Uuid::from_u128(num);
assert_eq!(id, from_str);
assert_eq!(id, from_simple);
assert_eq!(id, from_bytes);
assert_eq!(id, from_u128);
}
// Round-trip conversion
fn round_trip() {
let original = Uuid::new_v4();
// Through string
let s = original.to_string();
let parsed = Uuid::parse_str(&s).unwrap();
assert_eq!(original, parsed);
// Through bytes
let bytes = *original.as_bytes();
let from_bytes = Uuid::from_bytes(bytes);
assert_eq!(original, from_bytes);
// Through u128
let num = original.as_u128();
let from_u128 = Uuid::from_u128(num);
assert_eq!(original, from_u128);
}use uuid::Uuid;
// Generate unique IDs for entities
#[derive(Debug, Clone)]
struct Entity {
id: Uuid,
name: String,
}
impl Entity {
fn new(name: String) -> Self {
Self {
id: Uuid::new_v4(),
name,
}
}
}
// Use as database primary key
#[derive(Debug)]
struct Record {
id: Uuid,
data: String,
created_at: String,
}
impl Record {
fn new(data: String) -> Self {
Self {
id: Uuid::new_v4(),
data,
created_at: chrono::Utc::now().to_rfc3339(),
}
}
}
fn main() {
// Create entities with unique IDs
let e1 = Entity::new("First".to_string());
let e2 = Entity::new("Second".to_string());
println!("Entity 1: {} -> {}", e1.id, e1.name);
println!("Entity 2: {} -> {}", e2.id, e2.name);
// Store records
let records = vec![
Record::new("Data 1".to_string()),
Record::new("Data 2".to_string()),
Record::new("Data 3".to_string()),
];
for record in &records {
println!("Record {}: {}", record.id, record.data);
}
}
// Correlation IDs for distributed tracing
struct RequestContext {
request_id: Uuid,
trace_id: Uuid,
span_id: Uuid,
}
impl RequestContext {
fn new() -> Self {
Self {
request_id: Uuid::new_v4(),
trace_id: Uuid::new_v4(),
span_id: Uuid::new_v4(),
}
}
}
// Session tokens
struct Session {
id: Uuid,
user_id: Uuid,
expires_at: std::time::Instant,
}
impl Session {
fn new(user_id: Uuid, duration: std::time::Duration) -> Self {
Self {
id: Uuid::new_v4(),
user_id,
expires_at: std::time::Instant::now() + duration,
}
}
fn is_valid(&self) -> bool {
std::time::Instant::now() < self.expires_at
}
}use uuid::Uuid;
// Typically with SQLx, Diesel, or other ORMs
// Example with pseudo-database code
struct Database {
users: std::collections::HashMap<Uuid, User>,
}
#[derive(Debug, Clone)]
struct User {
id: Uuid,
username: String,
email: String,
}
impl Database {
fn new() -> Self {
Self {
users: std::collections::HashMap::new(),
}
}
fn create_user(&mut self, username: String, email: String) -> Uuid {
let id = Uuid::new_v4();
let user = User {
id,
username,
email,
};
self.users.insert(id, user);
id
}
fn get_user(&self, id: Uuid) -> Option<&User> {
self.users.get(&id)
}
fn delete_user(&mut self, id: Uuid) -> bool {
self.users.remove(&id).is_some()
}
fn find_by_email(&self, email: &str) -> Option<&User> {
self.users.values().find(|u| u.email == email)
}
}
fn database_example() {
let mut db = Database::new();
// Create users
let id1 = db.create_user("alice".to_string(), "alice@example.com".to_string());
let id2 = db.create_user("bob".to_string(), "bob@example.com".to_string());
println!("Created users: {} and {}", id1, id2);
// Retrieve user
if let Some(user) = db.get_user(id1) {
println!("Found: {:?}", user);
}
// Find by email
if let Some(user) = db.find_by_email("bob@example.com") {
println!("Found by email: {:?}", user);
}
// Delete user
db.delete_user(id1);
println!("Users after delete: {}", db.users.len());
}
// With SQLx (pseudo-code)
// Requires: uuid = { version = "1", features = ["v4"] }
//
// async fn create_user(pool: &sqlx::PgPool, username: &str) -> Result<Uuid, sqlx::Error> {
// let id = Uuid::new_v4();
// sqlx::query!(
// "INSERT INTO users (id, username) VALUES ($1, $2)",
// id,
// username
// )
// .execute(pool)
// .await?;
// Ok(id)
// }use uuid::Uuid;
fn main() {
// In tests, you might want deterministic UUIDs
// Create UUIDs from fixed strings
let test_id = Uuid::parse_str("00000000-0000-0000-0000-000000000001").unwrap();
println!("Test ID: {}", test_id);
// Or use nil as placeholder
let placeholder = Uuid::nil();
// Helper for tests
fn test_uuid(n: u8) -> Uuid {
let bytes = [
0, 0, 0, 0,
0, 0,
0, 0,
0, 0,
0, 0, 0, 0, 0, n,
];
Uuid::from_bytes(bytes)
}
let id1 = test_uuid(1);
let id2 = test_uuid(2);
println!("Test IDs: {}, {}", id1, id2);
}
#[cfg(test)]
mod tests {
use super::*;
use uuid::Uuid;
fn make_test_user(id: &str) -> User {
User {
id: Uuid::parse_str(id).unwrap(),
username: "test".to_string(),
email: "test@example.com".to_string(),
}
}
#[test]
fn test_user_creation() {
let user = make_test_user("550e8400-e29b-41d4-a716-446655440000");
assert_eq!(
user.id,
Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap()
);
}
}Uuid::new_v4() for random UUIDs (most common)Uuid::new_v5(namespace, name) for deterministic, hash-based UUIDsUuid::now_v7() for time-ordered UUIDs (newer, sortable)features = ["v4", "v5", "v7", "serde"]Uuid::parse_str() or .parse().hyphenated(), .simple(), .urn(), .braced().as_bytes() for a &[u8; 16].as_u128() and Uuid::from_u128().is_nil(), .get_version(), .get_variant()NAMESPACE_DNS, NAMESPACE_URL, etc.Copy, Clone, Eq, Hash, Ord for collection useserde feature for JSON serialization