Rust’s systems-programming focus makes identifier generation performance a first-class concern. The uuid crate provides zero-overhead UUID v4 and v7 generation, and both Diesel and SQLx support the Uuid type natively for PostgreSQL and MySQL backends.
Setting Up the uuid Crate
# Cargo.toml
[dependencies]
uuid = { version = "1", features = ["v4", "v7", "serde"] }
| Feature | Enables |
|---|---|
v4 | Uuid::new_v4() — random UUID |
v7 | Uuid::now_v7() — time-ordered UUID |
serde | Serialize / Deserialize for JSON and binary formats |
fast-rng | Faster RNG (still cryptographically adequate for v4) |
Generating UUIDs
use uuid::Uuid;
let v4 = Uuid::new_v4();
println!("{v4}"); // 550e8400-e29b-41d4-a716-446655440000
let v7 = Uuid::now_v7();
println!("{v7}"); // 018fbe3a-4c5d-7000-8abc-0123456789ab
Uuid::now_v7() reads std::time::SystemTime::now() for the 48-bit millisecond timestamp and fills the remaining 74 bits with randomness from the OS CSPRNG. Monotonic counter behaviour within a single millisecond is not implemented by default — if within-millisecond ordering matters, use Uuid::new_v7(Timestamp) with a manually managed counter.
Diesel Integration
Diesel’s PostgreSQL backend maps uuid::Uuid to the uuid column type:
[dependencies]
diesel = { version = "2", features = ["postgres", "uuid"] }
uuid = { version = "1", features = ["v7"] }
Schema (generated by diesel migration):
CREATE TABLE orders (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
customer_id UUID NOT NULL,
status VARCHAR NOT NULL
);
Rust model:
use diesel::prelude::*;
use uuid::Uuid;
#[derive(Queryable, Selectable)]
#[diesel(table_name = crate::schema::orders)]
pub struct Order {
pub id: Uuid,
pub customer_id: Uuid,
pub status: String,
}
#[derive(Insertable)]
#[diesel(table_name = crate::schema::orders)]
pub struct NewOrder {
pub id: Uuid,
pub customer_id: Uuid,
pub status: String,
}
fn create_order(conn: &mut PgConnection, customer_id: Uuid) -> QueryResult<Order> {
let new = NewOrder {
id: Uuid::now_v7(),
customer_id,
status: "pending".into(),
};
diesel::insert_into(orders::table)
.values(&new)
.get_result(conn)
}
SQLx Integration
SQLx maps uuid::Uuid to PostgreSQL’s uuid type via the PgHasArrayType and Encode/Decode traits:
[dependencies]
sqlx = { version = "0.8", features = ["postgres", "uuid", "runtime-tokio-rustls"] }
uuid = { version = "1", features = ["v7"] }
use sqlx::PgPool;
use uuid::Uuid;
async fn create_order(pool: &PgPool, customer_id: Uuid) -> sqlx::Result<Uuid> {
let id = Uuid::now_v7();
sqlx::query!(
"INSERT INTO orders (id, customer_id, status) VALUES ($1, $2, 'pending')",
id,
customer_id,
)
.execute(pool)
.await?;
Ok(id)
}
Serde JSON Serialization
With the serde feature, Uuid serializes to the standard hyphenated string:
use serde::{Deserialize, Serialize};
use uuid::Uuid;
#[derive(Serialize, Deserialize)]
struct OrderResponse {
id: Uuid, // serializes as "018fbe3a-4c5d-7000-8abc-0123456789ab"
status: String,
}
For compact binary protocols (MessagePack, CBOR), use uuid::serde::compact to serialize as 16 raw bytes instead of the 36-byte string.
Choosing v4 vs v7
| Use case | Version |
|---|---|
| PostgreSQL / MySQL primary key | v7 — index-friendly sequential inserts |
| External API identifier (URL, token) | v4 — creation time not leaked |
| Distributed trace / correlation ID | v7 — sortable across services |
| Idempotency key | v4 — must be unguessable |
| In-memory cache key | Either — no database index involved |
Related Resources
- uuid crate on crates.io
- uuid crate API documentation
- Diesel ORM documentation
- SQLx — async Rust SQL toolkit
- RFC 9562 — UUID v7 standard
Frequently asked questions
How do I generate UUID v7 in Rust?
Use the uuid crate with the v7 feature: uuid = { version = "1", features = ["v7"] }. Then call Uuid::now_v7(). For Diesel or SQLx integration, also enable the uuid feature on those crates — they map Uuid to the PostgreSQL uuid column type automatically.
Do UUIDs hurt database index performance?
UUID v4 does — its randomness causes every insert to land at a different leaf page, leading to page splits and poor cache locality. UUID v7 embeds a millisecond timestamp so consecutive inserts cluster together, behaving like an auto-increment integer for B-tree purposes.
Should I use v4 or v7?
Use v7 for database primary keys (time-sortable, index-friendly) and v4 for anything where creation order could leak information, like tokens or share links.
How do I generate a UUID v7 in Rust?
Add the uuid crate with the v7 feature: uuid = { version = "1", features = ["v7"] }. Then call Uuid::now_v7(). The crate reads the system clock via SystemTime::now() and fills the remaining 74 bits with CSPRNG randomness.
Does Diesel support UUID primary keys in Rust?
Yes. Enable the uuid feature on the diesel crate: diesel = { version = "2", features = ["postgres", "uuid"] }. Diesel maps uuid::Uuid to PostgreSQL's native uuid column type automatically. No custom type mapping or conversion is needed.