Skip to main content

Searching...

Tools
Articles
View All Results

Developer Lab · Rust

Generate UUID in Rust

The uuid crate on crates.io is the Rust standard - CSPRNG-backed, zero unsafe, no_std compatible, and serde-ready. Uuid::new_v4() in one line.

Quick Reference

Function Version Sortable Use Case
Uuid::new_v4() v4 No General purpose - session IDs, record IDs, API keys
Uuid::now_v7() v7 Yes Database PKs, event logs - time-ordered, B-tree friendly
Uuid::new_v5() v5 No Deterministic - same namespace + name always gives same UUID
Uuid::parse_str() any - Parse and validate an existing UUID string

Primary Implementation

Production Ready
rust
use uuid::Uuid;

fn main() {
    // UUID v4 - random, CSPRNG-backed via getrandom crate
    let id = Uuid::new_v4();
    println!("{}", id);
    // → f47ac10b-58cc-4372-a567-0e02b2c3d479

    // As a hyphenated string
    let id_str = id.to_string();

    // As a simple (no-hyphen) string
    let id_simple = id.simple().to_string();
    // → f47ac10b58cc4372a5670e02b2c3d479

    // As raw bytes (16 bytes, stack-allocated)
    let id_bytes: [u8; 16] = *id.as_bytes();

    // Parse and validate an existing UUID string
    match Uuid::parse_str("f47ac10b-58cc-4372-a567-0e02b2c3d479") {
        Ok(parsed) => println!("Version: {:?}", parsed.get_version()),
        Err(e)     => eprintln!("Invalid UUID: {}", e),
    }

    // Generate multiple UUIDs
    let ids: Vec = (0..5).map(|_| Uuid::new_v4()).collect();
}

All UUID Versions

UUID v4 - Random (recommended default)

rust
// Cargo.toml: uuid = { version = "1", features = ["v4"] }
use uuid::Uuid;

let id = Uuid::new_v4();
println!("{}", id); // → "550e8400-e29b-41d4-a716-446655440000"

UUID v7 - Time-ordered (database PKs)

rust
// Cargo.toml: uuid = { version = "1", features = ["v7"] }
use uuid::Uuid;

// Millisecond-precision timestamp prefix + random suffix
// IDs generated later always sort after earlier ones
let id = Uuid::now_v7();
println!("{}", id); // → "018e8f6a-1b2c-7d3e-9f4a-5b6c7d8e9f0a"

UUID v5 - Deterministic / Namespace-based

rust
// Cargo.toml: uuid = { version = "1", features = ["v5"] }
use uuid::Uuid;

// SHA-1 hash of namespace + name - same inputs always produce the same UUID
let id = Uuid::new_v5(&Uuid::NAMESPACE_DNS, b"example.com");
println!("{}", id); // → always "cfbff0d1-9375-5685-968c-48ce8b15ae17"

// Custom namespace for your application
let my_ns = Uuid::new_v4(); // generate once, store as a constant
let record_id = Uuid::new_v5(&my_ns, b"user:42");

UUID v3 - MD5 Namespace (legacy)

rust
// Cargo.toml: uuid = { version = "1", features = ["v3"] }
use uuid::Uuid;

// MD5-based - prefer v5 (SHA-1) for new code
let id = Uuid::new_v3(&Uuid::NAMESPACE_DNS, b"example.com");
println!("{}", id);

Real-World Use Cases

1. Axum web handler - request tracing ID

rust
use axum::{middleware::Next, response::Response, http::Request};
use uuid::Uuid;

// Axum middleware that injects a request ID into every response
pub async fn request_id_middleware<B>(
    mut req: Request<B>,
    next: Next<B>,
) -> Response {
    let request_id = Uuid::new_v4().to_string();
    req.headers_mut().insert(
        "x-request-id",
        request_id.parse().unwrap(),
    );
    let mut response = next.run(req).await;
    response.headers_mut().insert(
        "x-request-id",
        request_id.parse().unwrap(),
    );
    response
}

2. sqlx - UUID primary key in PostgreSQL

rust
use sqlx::PgPool;
use uuid::Uuid;
use serde::{Deserialize, Serialize};

// Cargo.toml: uuid = { version = "1", features = ["v4", "serde"] }
#[derive(Debug, Serialize, Deserialize)]
struct User {
    id: Uuid,   // Store as Uuid type, not String
    name: String,
}

async fn create_user(pool: &PgPool, name: &str) -> Result<User, sqlx::Error> {
    let user = sqlx::query_as!(
        User,
        "INSERT INTO users (id, name) VALUES ($1, $2) RETURNING id, name",
        Uuid::new_v4(),
        name
    )
    .fetch_one(pool)
    .await?;
    Ok(user)
}

3. Event sourcing - deterministic event IDs

rust
use uuid::Uuid;

const EVENT_NAMESPACE: Uuid = Uuid::from_bytes([
    0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1,
    0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8,
]);

// Idempotent: same event always gets the same UUID
fn event_id(source: &str, event_type: &str, timestamp: &str) -> Uuid {
    let key = format!("{}:{}:{}", source, event_type, timestamp);
    Uuid::new_v5(&EVENT_NAMESPACE, key.as_bytes())
}

fn main() {
    let id1 = event_id("payments", "charge.created", "2026-05-01T12:00:00Z");
    let id2 = event_id("payments", "charge.created", "2026-05-01T12:00:00Z");
    assert_eq!(id1, id2); // always true - safe to retry
}

Common Mistakes

Forgetting to enable the right Cargo features

The uuid crate ships with no features enabled by default. Calling Uuid::new_v4() without features = ["v4"] in Cargo.toml is a compile error. Enable exactly what you need: ["v4", "v7", "serde"].

Storing UUIDs as String instead of Uuid

The Uuid type is a 16-byte stack-allocated value. Storing it as a String (36 bytes + heap allocation) wastes memory and loses type safety. Pass Uuid around and only call .to_string() at the boundary.

Using UUID v1 in web services

UUID v1 embeds the server's MAC address and a timestamp, leaking network topology and making IDs predictable. Use v4 for random IDs or v7 for time-ordered IDs in web services.

How It Works

The uuid crate delegates entropy to the getrandom crate, which calls the OS CSPRNG directly - /dev/urandom on Linux/macOS and BCryptGenRandom on Windows. No userspace RNG state is maintained.

The Uuid type is a newtype wrapper around [u8; 16] - a 16-byte value type stored entirely on the stack. Zero heap allocation, zero unsafe code in the public API.

With the serde feature, Uuid automatically serializes to a hyphenated string in JSON and deserializes back, with validation included.

Output Formats

id.to_string() / format!("{}", id)

f47ac10b-58cc-4372-a567-0e02b2c3d479

id.simple().to_string()

f47ac10b58cc4372a5670e02b2c3d479

id.as_bytes() - [u8; 16]

[0xf4, 0x7a, 0xc1, 0x0b, ...]

id.as_u128()

324716517733474852975027497220825710713

Best Practices, Performance, and Security

Best practices

Store and pass Uuid values - only convert to string at API/DB boundaries.

Enable the serde feature for automatic JSON serialization with validation.

Use Uuid::now_v7() for database primary keys - sequential inserts avoid B-tree fragmentation.

Performance

Rust generates roughly 10–50 million UUIDs/second on modern hardware. The Uuid type is stack-allocated (16 bytes), so there is zero heap allocation per UUID.

The crate is no_std compatible with the getrandom feature, making it suitable for embedded targets.

Security

Entropy source: getrandom crate → OS CSPRNG (/dev/urandom on Linux/macOS, BCryptGenRandom on Windows). Cryptographically secure.

The uuid crate contains zero unsafe code in its public API. Suitable for session tokens, CSRF tokens, and security-sensitive IDs.

Installation

UUID v4 (uuid crate)

bash
# Cargo.toml
[dependencies]
uuid = { version = "1", features = ["v4", "v7", "serde"] }

The uuid crate is on crates.io. Enable only the features you need to keep compile times minimal.

Frequently Asked Questions

How do I generate a UUID in Rust?

The uuid crate on crates.io is the standard choice. Add uuid = { version = '1', features = ['v4', 'v7'] } to Cargo.toml, then call Uuid::new_v4() for a random UUID or Uuid::now_v7() for a time-ordered one. Enable the serde feature for serialization support.

Is Uuid::new_v4() cryptographically secure?

Yes. Uuid::new_v4() uses the platform CSPRNG (operating system secure random source), suitable for session tokens, API keys, and idempotency keys. Do not use non-cryptographic random sources for security-sensitive identifiers.

What is the difference between UUID v4 and v7 in Rust?

UUID v4 (Uuid::new_v4() or equivalent) is fully random and not sortable. UUID v7 embeds a millisecond timestamp for chronological sorting (RFC 9562). Use v4 for general-purpose IDs; use v7 for database primary keys at scale.

Do I need to install a package for UUID generation in Rust?

A package or library install may be required. See the Installation section for the recommended approach in Rust.

How do I validate a UUID string in Rust?

Use the platform's UUID parse/validation function, or test against the RFC 4122 regex: /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i. Always validate external input at API boundaries.

Should I use UUIDs as database primary keys in Rust applications?

UUIDs work well as primary keys for distributed systems. Prefer native UUID/BINARY(16) column types over VARCHAR(36). For very large tables, consider UUID v7 for better B-tree insert locality.

Can I generate UUIDs in Rust without a network connection?

Yes. UUID generation uses local OS entropy sources and does not require network access. Each call is independent and thread-safe on modern platforms.

What output formats are available in Rust?

The standard hyphenated lowercase string (36 chars) is the default. Most APIs also support 32-char hex (no hyphens) and 16-byte binary formats. Use string format for APIs and binary for database storage.

What RFC standards apply to Rust UUID generation?

Version 4 UUIDs follow RFC 4122. UUID v7 follows RFC 9562 (May 2024). Ensure your chosen method produces compliant version and variant bits.

When should I avoid UUID v1?

Avoid UUID v1 in security-sensitive contexts - it embeds MAC address and timestamp information. Prefer v4 (Uuid::new_v4() or equivalent) unless you need legacy Cassandra timeuuid compatibility.

Key definitions

UUID
128-bit universally unique identifier, usually shown as 36 hex characters with hyphens.
CSPRNG
Cryptographically secure pseudo-random number generator - the entropy source behind secure UUID generation.
RFC 4122
IETF standard defining UUID versions 1 through 5. Version 4 is random.
RFC 9562
IETF standard adding UUID versions 6, 7, and 8. Version 7 is time-ordered.