How does uuid::Uuid::parse_str validate UUID format and what error information does it provide?

uuid::Uuid::parse_str validates that a string conforms to the UUID format specification, accepting both hyphenated (canonical) and non-hyphenated (simple) representations. The function returns Result<Uuid, Error> where the error type provides detailed information about why parsing failed. Validation checks include: correct length (32 hex digits for simple format, 36 characters for hyphenated), valid hexadecimal characters, proper hyphen placement at positions 8, 13, 18, and 23, and optionally valid UUID version and variant fields. The error type distinguishes between different failure modes, returning specific error variants for invalid length, invalid characters, and incorrect group positions.

Basic parse_str Usage

use uuid::Uuid;
 
fn main() -> Result<(), uuid::Error> {
    // Parse a hyphenated UUID
    let uuid = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000")?;
    println!("Parsed: {}", uuid);
    
    // Parse a simple UUID (no hyphens)
    let uuid_simple = Uuid::parse_str("550e8400e29b41d4a716446655440000")?;
    println!("Parsed simple: {}", uuid_simple);
    
    Ok(())
}

parse_str accepts both hyphenated and simple UUID formats.

Valid UUID Formats

use uuid::Uuid;
 
fn main() {
    // Hyphenated (canonical) format: 8-4-4-4-12 hex digits
    let hyphenated = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000");
    assert!(hyphenated.is_ok());
    
    // Simple format: 32 hex digits without hyphens
    let simple = Uuid::parse_str("550e8400e29b41d4a716446655440000");
    assert!(simple.is_ok());
    
    // Uppercase is valid
    let upper = Uuid::parse_str("550E8400-E29B-41D4-A716-446655440000");
    assert!(upper.is_ok());
    
    // Mixed case is valid
    let mixed = Uuid::parse_str("550e8400-E29b-41D4-a716-446655440000");
    assert!(mixed.is_ok());
    
    // Braced format is also accepted (curly braces are stripped)
    let braced = Uuid::parse_str("{550e8400-e29b-41d4-a716-446655440000}");
    assert!(braced.is_ok());
    
    // URN format is also accepted
    let urn = Uuid::parse_str("urn:uuid:550e8400-e29b-41d4-a716-446655440000");
    assert!(urn.is_ok());
}

Multiple formats are accepted: hyphenated, simple, braced, and URN.

Error Type

use uuid::Uuid;
 
fn main() {
    let result = Uuid::parse_str("invalid");
    
    match result {
        Ok(uuid) => println!("UUID: {}", uuid),
        Err(e) => {
            // uuid::Error provides error information
            println!("Error kind: {:?}", e);
            println!("Error message: {}", e);
        }
    }
}

The error type is uuid::Error, which implements std::fmt::Display and std::fmt::Debug.

Invalid Length Error

use uuid::Uuid;
 
fn main() {
    // Too short
    let too_short = Uuid::parse_str("550e8400-e29b-41d4-a716");
    println!("Too short: {:?}", too_short);
    // Err(InvalidLength(20, Expected(32, 36)))
    
    // Too long
    let too_long = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000-extra");
    println!("Too long: {:?}", too_long);
    // Err(InvalidLength(42, Expected(32, 36)))
}

InvalidLength error reports the actual length and expected lengths.

Invalid Character Error

use uuid::Uuid;
 
fn main() {
    // Non-hexadecimal character
    let invalid_char = Uuid::parse_str("550e8400-g29b-41d4-a716-446655440000");
    println!("Invalid char: {:?}", invalid_char);
    // 'g' is not a valid hex character
    
    // Invalid character at specific position
    let with_space = Uuid::parse_str("550e8400-e29b-41d4-a716-44665544 000");
    println!("With space: {:?}", with_space);
    // Space is invalid
}

InvalidCharacter error reports the position and invalid character.

Invalid Group Position Error

use uuid::Uuid;
 
fn main() {
    // Hyphen in wrong position
    let wrong_hyphen = Uuid::parse_str("550e8400e-29b-41d4-a716-446655440000");
    println!("Wrong hyphen: {:?}", wrong_hyphen);
    // Hyphen at position 9 instead of 8
    
    // Missing hyphen where expected
    let missing_hyphen = Uuid::parse_str("550e8400e29b-41d4a716446655440000");
    println!("Missing hyphen: {:?}", missing_hyphen);
    // Hyphens must be at positions 8, 13, 18, 23
}

InvalidGroup error reports misplaced hyphens.

Error Variants

use uuid::Uuid;
 
fn main() {
    // The error provides context about what went wrong
    
    // InvalidLength(found, expected)
    let err1 = Uuid::parse_str("short").unwrap_err();
    println!("Length error: {}", err1);
    
    // InvalidCharacter(found, position)
    let err2 = Uuid::parse_str("550e8400-g29b-41d4-a716-446655440000").unwrap_err();
    println!("Character error: {}", err2);
    
    // InvalidGroup(found, expected, position) - for hyphen errors
    let err3 = Uuid::parse_str("550e8400e-29b-41d4-a716-446655440000").unwrap_err();
    println!("Group error: {}", err3);
}

Error variants provide specific information about the failure.

Parsing from Various Inputs

use uuid::Uuid;
 
fn main() -> Result<(), uuid::Error> {
    // From &str
    let from_str = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000")?;
    
    // From String (String derefs to &str)
    let from_string = Uuid::parse_str(&String::from("550e8400-e29b-41d4-a716-446655440000"))?;
    
    // From bytes (without parsing)
    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);
    // from_bytes doesn't parse - it directly constructs
    
    Ok(())
}

parse_str handles string input; from_bytes creates directly from bytes.

TryFrom and FromStr Traits

use uuid::Uuid;
use std::str::FromStr;
 
fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Using FromStr trait
    let uuid1 = "550e8400-e29b-41d4-a716-446655440000".parse::<Uuid>()?;
    
    // Using TryFrom
    let uuid2 = Uuid::try_from("550e8400-e29b-41d4-a716-446655440000")?;
    
    // Both use the same parsing logic as parse_str
    assert_eq!(uuid1, uuid2);
    
    Ok(())
}

Uuid implements FromStr and TryFrom<&str>, delegating to parse_str.

Hyphenated vs Simple Format

use uuid::Uuid;
 
fn main() {
    // Hyphenated: 36 characters (32 hex + 4 hyphens)
    // Groups: 8-4-4-4-12
    let hyphenated = "550e8400-e29b-41d4-a716-446655440000";
    
    // Simple: 32 characters (all hex digits)
    let simple = "550e8400e29b41d4a716446655440000";
    
    // Both parse to the same UUID
    let h = Uuid::parse_str(hyphenated).unwrap();
    let s = Uuid::parse_str(simple).unwrap();
    
    assert_eq!(h, s);
    assert_eq!(h.hyphenated().to_string(), hyphenated);
}

Hyphenated and simple formats represent the same UUID.

Braced and URN Formats

use uuid::Uuid;
 
fn main() {
    // Braced format: {uuid}
    let braced = Uuid::parse_str("{550e8400-e29b-41d4-a716-446655440000}");
    assert!(braced.is_ok());
    
    // URN format: urn:uuid:uuid
    let urn = Uuid::parse_str("urn:uuid:550e8400-e29b-41d4-a716-446655440000");
    assert!(urn.is_ok());
    
    // These are convenience formats that get normalized
    let uuid = braced.unwrap();
    println!("Hyphenated: {}", uuid.hyphenated());
}

Braced and URN formats are accepted and normalized.

Case Insensitivity

use uuid::Uuid;
 
fn main() {
    // All hex characters are case-insensitive
    
    let lowercase = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
    let uppercase = Uuid::parse_str("550E8400-E29B-41D4-A716-446655440000").unwrap();
    let mixed = Uuid::parse_str("550e8400-E29b-41D4-A716-446655440000").unwrap();
    
    // All produce the same UUID
    assert_eq!(lowercase, uppercase);
    assert_eq!(lowercase, mixed);
    
    // Display outputs lowercase by default
    println!("{}", lowercase); // 550e8400-e29b-41d4-a716-446655440000
}

Hex characters are case-insensitive; display is lowercase.

Valid Hex Characters

use uuid::Uuid;
 
fn main() {
    // Valid: 0-9, a-f, A-F
    
    // These are all valid
    assert!(Uuid::parse_str("00000000-0000-0000-0000-000000000000").is_ok());
    assert!(Uuid::parse_str("ffffffff-ffff-ffff-ffff-ffffffffffff").is_ok());
    assert!(Uuid::parse_str("FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF").is_ok());
    assert!(Uuid::parse_str("ABCDEF12-1234-5678-9ABC-DEF012345678").is_ok());
    
    // These are invalid (not hex characters)
    assert!(Uuid::parse_str("gggggggg-gggg-gggg-gggg-gggggggggggg").is_err());
    assert!(Uuid::parse_str("550e8400-z29b-41d4-a716-446655440000").is_err());
}

Only hexadecimal characters (0-9, a-f, A-F) are valid in UUID positions.

Version Field Validation

use uuid::Uuid;
 
fn main() {
    // Version is in the "4" position (6th octet, high nibble)
    // Format: xxxxxxxx-xxxx-Vxxx-xxxx-xxxxxxxxxxxx
    // V should be 1-5 for standard versions
    
    // Version 1 (time-based)
    let v1 = Uuid::parse_str("550e8400-e29b-11d4-a716-446655440000").unwrap();
    println!("Version: {}", v1.get_version().unwrap());
    
    // Version 4 (random)
    let v4 = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
    println!("Version: {}", v4.get_version().unwrap());
    
    // Version field is parsed but not validated by parse_str
    // Any value 0-15 is accepted
}

parse_str accepts any version number; version is parsed, not enforced.

Variant Field Validation

use uuid::Uuid;
 
fn main() {
    // Variant is in position 16-17 (8th octet, high bits)
    // Format: xxxxxxxx-xxxx-xxxx-Mxxx-xxxxxxxxxxxx
    // M = 8, 9, a, b for RFC 4122 variant
    
    // RFC 4122 variant (10xx = 8, 9, a, b)
    let rfc = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
    println!("Variant: {:?}", rfc.get_variant());
    
    // Microsoft variant (110x = c, d, e, f)
    let ms = Uuid::parse_str("550e8400-e29b-41d4-c716-446655440000").unwrap();
    
    // Reserved variant (111x = f)
    let reserved = Uuid::parse_str("550e8400-e29b-41d4-f716-446655440000").unwrap();
    
    // parse_str accepts any variant
}

parse_str accepts any variant; variant is parsed, not enforced.

Nil UUID

use uuid::Uuid;
 
fn main() {
    // Nil UUID (all zeros) is valid
    let nil = Uuid::parse_str("00000000-0000-0000-0000-000000000000").unwrap();
    assert!(nil.is_nil());
    
    // Also valid in simple format
    let nil_simple = Uuid::parse_str("00000000000000000000000000000000").unwrap();
    assert!(nil_simple.is_nil());
    
    // Can create directly
    let nil_direct = Uuid::nil();
    assert_eq!(nil, nil_direct);
}

The nil UUID (all zeros) is valid and represents "no UUID".

Error Handling Patterns

use uuid::Uuid;
use std::fmt;
 
// Custom error wrapper for better error messages
#[derive(Debug)]
enum ParseError {
    InvalidUuid(uuid::Error),
}
 
impl fmt::Display for ParseError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            ParseError::InvalidUuid(e) => write!(f, "Invalid UUID format: {}", e),
        }
    }
}
 
impl std::error::Error for ParseError {}
 
fn parse_user_id(input: &str) -> Result<Uuid, ParseError> {
    Uuid::parse_str(input).map_err(ParseError::InvalidUuid)
}
 
fn main() -> Result<(), ParseError> {
    let uuid = parse_user_id("550e8400-e29b-41d4-a716-446655440000")?;
    println!("User ID: {}", uuid);
    Ok(())
}

Wrap uuid::Error for domain-specific error handling.

Detailed Error Information

use uuid::Uuid;
 
fn main() {
    let result = Uuid::parse_str("not-a-uuid");
    
    if let Err(e) = result {
        // Error implements Display and Debug
        println!("Error: {}", e);      // Human-readable
        println!("Debug: {:?}", e);    // Detailed info
        
        // The error message describes what was expected
        // "invalid length: expected 32 or 36 characters, found 10"
    }
    
    // Different errors give different messages
    let err_length = Uuid::parse_str("550e8400").unwrap_err();
    println!("Length error: {}", err_length);
    
    let err_char = Uuid::parse_str("550e8400-g29b-41d4-a716-446655440000").unwrap_err();
    println!("Character error: {}", err_char);
    
    let err_group = Uuid::parse_str("550e8400e-29b-41d4-a716-446655440000").unwrap_err();
    println!("Group error: {}", err_group);
}

Error messages are descriptive and indicate the specific issue.

Common Validation Mistakes

use uuid::Uuid;
 
fn main() {
    // Mistake: Extra whitespace
    let with_whitespace = Uuid::parse_str(" 550e8400-e29b-41d4-a716-446655440000");
    assert!(with_whitespace.is_err());
    
    // Mistake: Uppercase URN
    let urn_upper = Uuid::parse_str("URN:UUID:550e8400-e29b-41d4-a716-446655440000");
    // URN prefix should be lowercase
    // Actually: "urn" prefix is case-insensitive, "uuid" should be lowercase
    
    // Mistake: Wrong hyphen positions
    let wrong_hyphens = Uuid::parse_str("550-e8400e-29b-41d4-a716-4466-55440000");
    assert!(wrong_hyphens.is_err());
    
    // Correct format reminder:
    // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
    // 8-4-4-4-12 hex digits
}

Common parsing errors include whitespace, wrong hyphen positions, and invalid characters.

Performance Considerations

use uuid::Uuid;
 
fn main() {
    let valid_uuid = "550e8400-e29b-41d4-a716-446655440000";
    
    // parse_str validates and constructs
    let start = std::time::Instant::now();
    for _ in 0..100_000 {
        let _ = Uuid::parse_str(valid_uuid);
    }
    println!("parse_str: {:?}", start.elapsed());
    
    // If you have bytes already, use from_bytes (no parsing)
    let bytes: [u8; 16] = [
        0x55, 0x0e, 0x84, 0x00, 0xe2, 0x9b, 0x41, 0xd4,
        0xa7, 0x16, 0x44, 0x66, 0x55, 0x44, 0x00, 0x00,
    ];
    let start = std::time::Instant::now();
    for _ in 0..100_000 {
        let _ = Uuid::from_bytes(bytes);
    }
    println!("from_bytes: {:?}", start.elapsed());
    
    // from_bytes is faster because it skips validation
}

from_bytes is faster than parse_str but requires valid bytes.

Synthesis

Validation process:

  1. Length check: Must be 32 (simple) or 36 (hyphenated) characters
  2. Character validation: All hex positions must contain 0-9, a-f, or A-F
  3. Hyphen validation: If hyphenated, hyphens must be at positions 8, 13, 18, 23
  4. Format normalization: Braced and URN formats are stripped

Error types:

  • InvalidLength: Wrong number of characters
  • InvalidCharacter: Non-hex character found
  • InvalidGroup: Hyphen in wrong position

Accepted formats:

  • Hyphenated: 550e8400-e29b-41d4-a716-446655440000
  • Simple: 550e8400e29b41d4a716446655440000
  • Braced: {550e8400-e29b-41d4-a716-446655440000}
  • URN: urn:uuid:550e8400-e29b-41d4-a716-446655440000

Best practices:

  • Use parse_str for untrusted input
  • Use from_bytes when you control the input source
  • Handle errors explicitly for user-facing validation
  • Use Uuid::nil() for the special nil UUID case

Version and variant: parse_str accepts any version (0-15) and variant values—they are parsed and accessible via get_version() and get_variant(), but not enforced during parsing.