How does uuid::Uuid::parse_str validate and parse UUID strings in various formats?
uuid::Uuid::parse_str accepts multiple UUID string formatsβsimple (32 hex digits), hyphenated (canonical 8-4-4-4-12), braced, and URN-prefixedβvalidating each character as hexadecimal and checking length constraints. The parser normalizes all valid formats to a consistent internal representation while rejecting malformed input with detailed error information.
Basic Parsing
use uuid::Uuid;
fn basic_parsing() {
// Hyphenated format (canonical)
let uuid = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
println!("Parsed: {}", uuid);
// Simple format (no hyphens)
let uuid = Uuid::parse_str("550e8400e29b41d4a716446655440000").unwrap();
println!("Parsed: {}", uuid);
// Both produce identical results:
assert_eq!(
Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap(),
Uuid::parse_str("550e8400e29b41d4a716446655440000").unwrap()
);
}The parser accepts both hyphenated and simple formats, producing identical Uuid values.
Supported Formats
use uuid::Uuid;
fn supported_formats() {
// Format 1: Hyphenated (canonical, RFC 4122)
let hyphenated = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000");
assert!(hyphenated.is_ok());
// Format 2: Simple (32 hex digits, no hyphens)
let simple = Uuid::parse_str("550e8400e29b41d4a716446655440000");
assert!(simple.is_ok());
// Format 3: Braced (Microsoft-style)
let braced = Uuid::parse_str("{550e8400-e29b-41d4-a716-446655440000}");
assert!(braced.is_ok());
// Format 4: URN (RFC 4122 URN namespace)
let urn = Uuid::parse_str("urn:uuid:550e8400-e29b-41d4-a716-446655440000");
assert!(urn.is_ok());
// All produce the same UUID
assert_eq!(hyphenated.unwrap(), simple.unwrap());
assert_eq!(hyphenated.unwrap(), braced.unwrap());
assert_eq!(hyphenated.unwrap(), urn.unwrap());
}The parser handles four formats, all normalizing to the same UUID.
Format Detection and Parsing
use uuid::Uuid;
fn format_detection() {
// The parser automatically detects format from prefix/content
// No prefix = hyphenated or simple
let uuid1 = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
// Brace prefix detected
let uuid2 = Uuid::parse_str("{550e8400-e29b-41d4-a716-446655440000}").unwrap();
// URN prefix detected
let uuid3 = Uuid::parse_str("urn:uuid:550e8400-e29b-41d4-a716-446655440000").unwrap();
// Parser strips prefix/brackets, then validates content
assert_eq!(uuid1, uuid2);
assert_eq!(uuid1, uuid3);
// Format detection is case-insensitive
let urn_upper = Uuid::parse_str("URN:UUID:550e8400-e29b-41d4-a716-446655440000");
assert!(urn_upper.is_ok());
}The parser detects format from prefixes and handles case-insensitive URN identifiers.
Hexadecimal Validation
use uuid::Uuid;
fn hexadecimal_validation() {
// Valid: all hexadecimal characters
let valid = Uuid::parse_str("550e8400e29b41d4a716446655440000");
assert!(valid.is_ok());
// Valid: uppercase hex
let upper = Uuid::parse_str("550E8400E29B41D4A716446655440000");
assert!(upper.is_ok());
// Valid: mixed case
let mixed = Uuid::parse_str("550e8400E29B41d4A716446655440000");
assert!(mixed.is_ok());
// Invalid: non-hex characters
let invalid = Uuid::parse_str("550e8400e29b41d4a71644665544000g");
// 'g' is not a valid hex character
assert!(invalid.is_err());
// Invalid: hyphen in wrong position (simple format)
let bad_hyphen = Uuid::parse_str("550e8400e29b-41d4a716446655440000");
// Hyphen in position 12 when not expected
// This is actually invalid for simple format
}Each character must be a valid hexadecimal digit (0-9, a-f, A-F) or a hyphen in expected positions.
Length Validation
use uuid::Uuid;
fn length_validation() {
// Valid lengths:
// - Simple: 32 hex digits
// - Hyphenated: 32 hex digits + 4 hyphens = 36 chars
// - Braced: 36 + 2 brackets = 38 chars
// - URN: varies based on prefix
// Correct simple format: 32 chars
let simple = Uuid::parse_str("550e8400e29b41d4a716446655440000");
assert!(simple.is_ok());
// Too short: 31 chars
let short = Uuid::parse_str("550e8400e29b41d4a71644665544000");
assert!(short.is_err());
// Too long: 33 chars
let long = Uuid::parse_str("550e8400e29b41d4a7164466554400000");
assert!(long.is_err());
// Correct hyphenated: 36 chars
let hyphenated = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000");
assert!(hyphenated.is_ok());
// Wrong hyphen positions
let bad_positions = Uuid::parse_str("550e8400e-29b-41d4-a716-446655440000");
assert!(bad_positions.is_err());
}Length and hyphen positions are strictly validated for hyphenated format.
Hyphen Position Validation
use uuid::Uuid;
fn hyphen_positions() {
// Canonical hyphenated format: 8-4-4-4-12
// Hyphens at positions: 8, 13, 18, 23 (0-indexed)
// Valid: hyphens in correct positions
let valid = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000");
assert!(valid.is_ok());
// Positions: 550e8400 - e29b - 41d4 - a716 - 446655440000
// Index: 0 7 9 12 14 17 19 22 24 35
// Invalid: missing hyphen
let missing = Uuid::parse_str("550e8400e29b-41d4-a716-446655440000");
assert!(missing.is_err());
// Invalid: extra hyphen
let extra = Uuid::parse_str("550e-8400-e29b-41d4-a716-446655440000");
assert!(extra.is_err());
// Invalid: wrong positions
let wrong = Uuid::parse_str("550e8400e-29-b41d4a7164-446655440000");
assert!(wrong.is_err());
}Hyphens must appear at exact positions in hyphenated format.
Error Handling
use uuid::Uuid;
fn error_handling() {
// parse_str returns Result<Uuid, Error>
// Valid UUID
match Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000") {
Ok(uuid) => println!("Parsed: {}", uuid),
Err(e) => println!("Error: {}", e),
}
// Invalid: non-hex character
match Uuid::parse_str("550e8400-xxxx-41d4-a716-446655440000") {
Ok(uuid) => println!("Parsed: {}", uuid),
Err(e) => println!("Error: {}", e),
// Output: Error: invalid character: expected an optional prefix of
// `urn:uuid:` followed by [0-9a-fA-F-], found `x` at 9
}
// Invalid: wrong length
match Uuid::parse_str("550e8400-e29b-41d4-a716") {
Ok(uuid) => println!("Parsed: {}", uuid),
Err(e) => println!("Error: {}", e),
// Output: Error: invalid length: expected 32 characters for simple
// format or 36 characters for hyphenated format
}
// Invalid: wrong hyphen positions
match Uuid::parse_str("550e-8400-e29b-41d4-a716-446655440000") {
Ok(uuid) => println!("Parsed: {}", uuid),
Err(e) => println!("Error: {}", e),
}
}Error messages indicate the specific validation failure.
Case Insensitivity
use uuid::Uuid;
fn case_insensitivity() {
// Lowercase
let lower = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
// Uppercase
let upper = Uuid::parse_str("550E8400-E29B-41D4-A716-446655440000").unwrap();
// Mixed case
let mixed = Uuid::parse_str("550e8400-E29b-41D4-a716-446655440000").unwrap();
// All produce identical UUIDs
assert_eq!(lower, upper);
assert_eq!(lower, mixed);
// Display format is lowercase by default
println!("Display: {}", lower); // lowercase
// Hyphenated format preserved case-insensitively
assert_eq!(lower.hyphenated().to_string(), "550e8400-e29b-41d4-a716-446655440000");
}The parser is case-insensitive for hexadecimal characters and URN prefixes.
Braced Format Handling
use uuid::Uuid;
fn braced_format() {
// Microsoft-style braced format
let braced = Uuid::parse_str("{550e8400-e29b-41d4-a716-446655440000}").unwrap();
let normal = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
assert_eq!(braced, normal);
// Braces must be complete
let no_close = Uuid::parse_str("{550e8400-e29b-41d4-a716-446655440000");
assert!(no_close.is_err());
let no_open = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000}");
assert!(no_open.is_err());
// Only hyphenated content inside braces
let braced_simple = Uuid::parse_str("{550e8400e29b41d4a716446655440000}");
// May or may not be accepted depending on version
// Typically requires hyphenated format inside braces
}Braced format expects hyphenated UUID content between { and }.
URN Format Handling
use uuid::Uuid;
fn urn_format() {
// RFC 4122 URN format
let urn = Uuid::parse_str("urn:uuid:550e8400-e29b-41d4-a716-446655440000").unwrap();
let normal = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
assert_eq!(urn, normal);
// Case-insensitive URN prefix
let urn_upper = Uuid::parse_str("URN:UUID:550e8400-e29b-41d4-a716-446655440000");
assert!(urn_upper.is_ok());
let urn_mixed = Uuid::parse_str("Urn:Uuid:550e8400-e29b-41d4-a716-446655440000");
assert!(urn_mixed.is_ok());
// URN requires hyphenated UUID after prefix
let urn_simple = Uuid::parse_str("urn:uuid:550e8400e29b41d4a716446655440000");
// Typically requires hyphenated format after urn:uuid:
}URN format strips the urn:uuid: prefix before parsing the UUID content.
Nil UUID Handling
use uuid::Uuid;
fn nil_uuid() {
// Nil UUID: all zeros
let nil_hyphenated = Uuid::parse_str("00000000-0000-0000-0000-000000000000").unwrap();
let nil_simple = Uuid::parse_str("00000000000000000000000000000000").unwrap();
assert_eq!(nil_hyphenated, nil_simple);
assert!(nil_hyphenated.is_nil());
// Can also use Uuid::nil()
let nil_direct = Uuid::nil();
assert_eq!(nil_hyphenated, nil_direct);
}The nil UUID (all zeros) is parsed correctly in all formats.
UUID Version Handling
use uuid::Uuid;
fn version_handling() {
// Version 4 (random) UUID
let v4 = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
// Version is encoded in the UUID (bits 48-51 of time_hi_and_version)
println!("Version: {:?}", v4.get_version());
println!("Variant: {:?}", v4.get_variant());
// Version digits: 4th segment starts with version
// 550e8400-e29b-41d4-a716-446655440000
// ^ 4 is version (random)
// Valid versions: 1-7 (as of UUID specification)
// Version bits must be valid
// Any hex in version position is accepted for parsing
// Version validation happens after parsing
let custom = Uuid::parse_str("550e8400-e29b-c1d4-a716-446655440000");
assert!(custom.is_ok()); // 'c' in version position
// Variant bits also validated
// RFC 4122 variant: 10xx in variant field
}The parser accepts any valid hex; version and variant are informational.
Variant Validation
use uuid::Uuid;
fn variant_validation() {
// Variant is encoded in certain bits
// RFC 4122 variant: 10xx (most significant bits of clock_seq)
let uuid = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
match uuid.get_variant() {
Some(variant) => println!("Variant: {:?}", variant),
None => println!("Unknown variant"),
}
// Valid variants:
// - NCS (reserved)
// - RFC 4122 (standard)
// - Microsoft
// - Future (reserved)
// Parser accepts any variant bits
// Use get_variant() to check after parsing
}Variant bits are parsed and can be queried after successful parsing.
Performance Characteristics
use uuid::Uuid;
fn performance_characteristics() {
// Parsing involves:
// 1. Length check
// 2. Format detection (prefix/brackets)
// 3. Hex validation per character
// 4. Hyphen position validation (if hyphenated)
// 5. Conversion to bytes
// Hyphenated format: 36 chars + validation
// Simple format: 32 chars (slightly faster)
// For hot paths, consider:
// 1. Pre-validate once, store as Uuid
// 2. Use Uuid::from_bytes if you have bytes
// From bytes (no parsing overhead)
let bytes = [0x55, 0x0e, 0x84, 0x00, 0xe2, 0x9b, 0x41, 0xd4,
0xa7, 0x16, 0x44, 0x66, 0x55, 0x44, 0x00, 0x00];
let from_bytes = Uuid::from_bytes(bytes);
// Parsing has overhead
let parsed = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
assert_eq!(from_bytes, parsed);
}For performance-critical code, use from_bytes to avoid parsing overhead.
Parsing Algorithm
use uuid::Uuid;
fn parsing_algorithm_explanation() {
// Internal parsing steps:
// 1. Trim and detect format
// - Check for "urn:uuid:" prefix (case-insensitive)
// - Check for '{' prefix (braced format)
// - Strip prefix and/or braces
// 2. Determine format
// - Length 32: simple format
// - Length 36: hyphenated format
// - Other: invalid
// 3. Validate characters
// - Simple: all must be hex digits
// - Hyphenated: hyphens at positions 8, 13, 18, 23
// everything else must be hex
// 4. Convert to bytes
// - Parse pairs of hex characters
// - Convert to 16 bytes
// Example: "550e8400"
// '5' '5' -> 0x55
// '0' 'e' -> 0x0e
// '8' '4' -> 0x84
// '0' '0' -> 0x00
// Result: [0x55, 0x0e, 0x84, 0x00]
let uuid = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
let bytes = uuid.as_bytes();
// bytes = [0x55, 0x0e, 0x84, 0x00, 0xe2, 0x9b, ...]
}The parser converts hex pairs to bytes after validating format.
Alternative Parsing Methods
use uuid::Uuid;
fn alternative_parsing() {
// parse_str: &str -> Result<Uuid, Error>
let result = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000");
// parse: Generic (supports String, etc.)
let from_string = "550e8400-e29b-41d4-a716-446655440000".parse::<Uuid>();
assert!(from_string.is_ok());
// from_bytes: [u8; 16] -> Uuid (no validation)
let bytes: [u8; 16] = [0x55, 0x0e, 0x84, 0x00, 0xe2, 0x9b,
0x41, 0xd4, 0xa7, 0x16, 0x44, 0x66,
0x55, 0x44, 0x00, 0x00];
let from_bytes = Uuid::from_bytes(bytes);
// from_bytes_le: Little-endian bytes
let from_bytes_le = Uuid::from_bytes_le(bytes);
// from_u128: 128-bit integer -> Uuid
let from_u128 = Uuid::from_u128(0x550e8400_e29b_41d4_a716_446655440000);
// All produce same UUID (with different byte ordering for le)
}Multiple construction methods provide flexibility based on data source.
Stricter Parsing with Features
use uuid::Uuid;
fn feature_flags() {
// The uuid crate has optional features for stricter validation:
// Default: Lenient parsing (accepts multiple formats)
// - Simple (32 hex digits)
// - Hyphenated (36 chars with hyphens)
// - Braced (with {})
// - URN (with urn:uuid: prefix)
// Feature: stricter parsing may be available
// Check crate documentation for feature flags
// Standard format (RFC 4122): hyphenated
let hyphenated = "550e8400-e29b-41d4-a716-446655440000";
// For strict hyphenated-only, validate manually:
fn parse_strict(s: &str) -> Result<Uuid, String> {
if s.len() != 36 || !s.chars().enumerate().all(|(i, c)| {
if i == 8 || i == 13 || i == 18 || i == 23 {
c == '-'
} else {
c.is_ascii_hexdigit()
}
}) {
return Err("Invalid UUID format".to_string());
}
Uuid::parse_str(s).map_err(|e| e.to_string())
}
}The default parser is lenient; manual validation enforces stricter format requirements.
Common Parsing Errors
use uuid::Uuid;
fn common_errors() {
// Error: Invalid length (too short)
let too_short = Uuid::parse_str("550e8400");
assert!(too_short.is_err());
// Error: Invalid length (too long)
let too_long = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000-extra");
assert!(too_long.is_err());
// Error: Invalid character
let bad_char = Uuid::parse_str("550e8400-xxxx-41d4-a716-446655440000");
assert!(bad_char.is_err());
// Error: Invalid hyphen position
let bad_hyphen = Uuid::parse_str("550e-8400-e29b-41d4-a716-446655440000");
assert!(bad_hyphen.is_err());
// Error: Missing hyphen (expected hyphenated)
let missing_hyphen = Uuid::parse_str("550e8400e29b41d4-a716-446655440000");
// This might parse as invalid length or wrong format
assert!(missing_hyphen.is_err());
// Error: Empty string
let empty = Uuid::parse_str("");
assert!(empty.is_err());
// Error: Only whitespace
let whitespace = Uuid::parse_str(" ");
assert!(whitespace.is_err());
// Error: Missing closing brace
let unclosed_brace = Uuid::parse_str("{550e8400-e29b-41d4-a716-446655440000");
assert!(unclosed_brace.is_err());
}Common errors include wrong length, invalid characters, and misplaced hyphens.
Real-World Usage Patterns
use uuid::Uuid;
use std::collections::HashMap;
struct User {
id: Uuid,
name: String,
}
fn real_world_patterns() {
// Pattern 1: Parse user input
fn parse_user_uuid(input: &str) -> Result<Uuid, String> {
Uuid::parse_str(input).map_err(|e| format!("Invalid UUID: {}", e))
}
// Pattern 2: Database key
let mut users: HashMap<Uuid, User> = HashMap::new();
if let Ok(id) = parse_user_uuid("550e8400-e29b-41d4-a716-446655440000") {
users.insert(id, User { id, name: "Alice".into() });
}
// Pattern 3: API deserialization (serde)
// #[derive(Deserialize)]
// struct Request {
// #[serde(deserialize_with = "Uuid::parse_str")]
// id: Uuid,
// }
// Pattern 4: Config file UUID
fn load_config_uuid(config_value: &str) -> Uuid {
Uuid::parse_str(config_value)
.expect("Config must contain valid UUID")
}
}UUID parsing is common for user input, database keys, APIs, and configuration.
Display and Serialization
use uuid::Uuid;
fn display_serialization() {
let uuid = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
// Display: hyphenated lowercase
println!("Display: {}", uuid); // 550e8400-e29b-41d4-a716-446655440000
// Hyphenated format
let hyphenated = uuid.hyphenated();
println!("Hyphenated: {}", hyphenated);
// Simple format (no hyphens)
let simple = uuid.simple();
println!("Simple: {}", simple); // 550e8400e29b41d4a716446655440000
// URN format
let urn = uuid.urn();
println!("URN: {}", urn); // urn:uuid:550e8400-e29b-41d4-a716-446655440000
// Braced format
let braced = uuid.braced();
println!("Braced: {}", braced); // {550e8400-e29b-41d4-a716-446655440000}
// Upper case variants
println!("Uppercase: {}", uuid.hyphenated().to_uppercase());
// 550E8400-E29B-41D4-A716-446655440000
}Parsed UUIDs can be formatted in any supported format.
Summary Table
fn summary_table() {
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// β Format β Example β Length β
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
// β Simple β 550e8400e29b41d4a716446655440000 β 32 β
// β Hyphenated β 550e8400-e29b-41d4-a716-446655440000 β 36 β
// β Braced β {550e8400-e29b-41d4-a716-446655440000} β 38 β
// β URN β urn:uuid:550e8400-e29b-41d4-a716-446655440000β 45 β
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// β Validation Step β Checks β
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
// β Length β 32 (simple) or 36 (hyphenated) + prefix β
// β Characters β Hex digits (0-9, a-f, A-F) β
// β Hyphens β Positions 8, 13, 18, 23 (hyphenated) β
// β Braces β Paired { } around content β
// β URN prefix β "urn:uuid:" (case-insensitive) β
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
}Summary
fn key_points() {
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// β Aspect β Behavior β
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
// β Formats β Simple, hyphenated, braced, URN β
// β Case β Case-insensitive for hex and URN β
// β Validation β Length, characters, hyphen positions β
// β Errors β Descriptive messages for failures β
// β Output β Normalized Uuid type β
// β Performance β Parsing has overhead; use from_bytes for speed β
// β Display β Multiple format options for output β
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// Key points:
// 1. parse_str accepts 4 formats: simple, hyphenated, braced, URN
// 2. All formats normalize to identical Uuid values
// 3. Hyphens must be at positions 8, 13, 18, 23 (hyphenated format)
// 4. Simple format requires exactly 32 hex characters
// 5. Parser is case-insensitive for hex characters
// 6. URN prefix "urn:uuid:" is case-insensitive
// 7. Braces must be paired { }
// 8. Error messages indicate specific validation failure
// 9. Use from_bytes() to avoid parsing overhead
// 10. Display provides multiple output formats
}Key insight: Uuid::parse_str is a lenient parser that accepts multiple common UUID string formatsβsimple (32 hex digits), hyphenated (RFC 4122 canonical format with hyphens at positions 8, 13, 18, 23), braced (Microsoft-style {uuid}), and URN-prefixed (urn:uuid:...)βnormalizing all to a consistent Uuid value. The parser validates that non-hyphen characters are valid hexadecimal digits (case-insensitive), hyphens appear in correct positions, and total length matches expected format. For performance-critical paths where UUIDs are already in byte form, use Uuid::from_bytes or Uuid::from_u128 to skip parsing overhead entirely. The lenient parsing approach means you can accept UUIDs from various sources (user input, APIs, configuration files) without pre-formatting, but if you need strict format enforcement, validate format before parsing.
