Try it yourself with our free Toml Formatter tool — runs entirely in your browser, no signup needed.

How to Parse TOML in Rust

How to Parse TOML in Rust

TOML (Tom's Obvious, Minimal Language) is a lightweight, easy-to-read configuration file format that has gained popularity in recent years. As a Rust developer, you may encounter TOML files in various projects, and parsing them efficiently is crucial. In this article, we'll explore how to parse TOML in Rust, covering the basics, common edge cases, and performance tips.

Quick Example

Here's a minimal example to get you started:

use toml;

fn main() {
    let toml_str = r#"
        title = "My Config"
        [database]
        host = "localhost"
        port = 5432
    "#;
    let config: toml::Value = toml::from_str(toml_str).unwrap();
    println!("{:?}", config);
}

This code parses a TOML string and prints the resulting toml::Value instance.

Step-by-Step Breakdown

Let's dissect the code:

  1. use toml;: We import the toml crate, which provides the TOML parsing functionality.
  2. let toml_str = r#"...";: We define a TOML string using a raw string literal (`r#"..."). This allows us to write the TOML content without escaping quotes.
  3. let config: toml::Value = toml::from_str(toml_str).unwrap();: We use the toml::from_str function to parse the TOML string into a toml::Value instance. The unwrap method is used to handle any errors that might occur during parsing. In a real-world application, you should handle errors more robustly.
  4. println!("{:?}", config);: We print the parsed toml::Value instance using the {:?} debug formatter.

Handling Edge Cases

Empty/Null Input

When dealing with empty or null input, the parser will return an error:

let toml_str = "";
let config: toml::Value = toml::from_str(toml_str).unwrap(); // Error: "unexpected end of input"

To handle this, you can add a simple check before parsing:

if toml_str.is_empty() {
    // Handle empty input
} else {
    let config: toml::Value = toml::from_str(toml_str).unwrap();
}

Invalid Input

If the input is invalid TOML, the parser will also return an error:

let toml_str = " invalid toml";
let config: toml::Value = toml::from_str(toml_str).unwrap(); // Error: " expected a table key"

You can handle this by using the Result type instead of unwrap:

let config: Result<toml::Value, toml::de::Error> = toml::from_str(toml_str);
match config {
    Ok(config) => println!("{:?}", config),
    Err(err) => println!("Error: {}", err),
}

Large Input

For large TOML files, you may want to consider using a streaming parser to avoid loading the entire file into memory:

use toml::de::Parser;

let mut parser = Parser::new(toml_str);
let mut config = toml::Value::new_table();
while let Some(event) = parser.next() {
    match event {
        toml::Event::StartTable => {
            // Handle table start
        }
        toml::Event::EndTable => {
            // Handle table end
        }
        toml::Event::Key(key) => {
            // Handle key
        }
        toml::Event::Value(value) => {
            // Handle value
        }
    }
}

Unicode/Special Characters

TOML supports Unicode characters, but you need to ensure that your Rust code is configured to handle them correctly:

let toml_str = "title = \"Hëllo Wørld\"";
let config: toml::Value = toml::from_str(toml_str).unwrap();
println!("{:?}", config); // prints "Hëllo Wørld"

Common Mistakes

1. Not handling errors

Don't use unwrap in production code:

let config: toml::Value = toml::from_str(toml_str).unwrap(); // Bad practice

Instead, handle errors using Result or Option:

let config: Result<toml::Value, toml::de::Error> = toml::from_str(toml_str);
match config {
    Ok(config) => println!("{:?}", config),
    Err(err) => println!("Error: {}", err),
}

2. Not checking for empty input

Don't assume that the input is always valid:

let toml_str = "";
let config: toml::Value = toml::from_str(toml_str).unwrap(); // Error

Add a simple check before parsing:

if toml_str.is_empty() {
    // Handle empty input
} else {
    let config: toml::Value = toml::from_str(toml_str).unwrap();
}

3. Not using the correct parser

Don't use the wrong parser for the job:

let toml_str = "invalid toml";
let config: toml::Value = toml::from_str(toml_str).unwrap(); // Error

Use the correct parser for your specific use case.

Performance Tips

1. Use the streaming parser

For large TOML files, use the streaming parser to avoid loading the entire file into memory:

use toml::de::Parser;

let mut parser = Parser::new(toml_str);
let mut config = toml::Value::new_table();
while let Some(event) = parser.next() {
    // Handle events
}

2. Avoid unnecessary allocations

Minimize allocations by using &str instead of String:

let toml_str = "title = \"Hello World\"";
let config: toml::Value = toml::from_str(toml_str).unwrap();

3. Use the toml crate's built-in optimizations

The toml crate has built-in optimizations for common use cases. Use the provided APIs to take advantage of these optimizations:

let toml_str = "title = \"Hello World\"";
let config: toml::Value = toml::from_str(toml_str).unwrap();

FAQ

Q: What is the best way to handle errors when parsing TOML?

A: Use the Result type or Option to handle errors instead of unwrap.

Q: How do I parse large TOML files?

A: Use the streaming parser to avoid loading the entire file into memory.

Q: Can I use TOML with Unicode characters?

A: Yes, TOML supports Unicode characters. Ensure that your Rust code is configured to handle them correctly.

Q: What is the difference between toml::Value and toml::Table?

A: toml::Value represents a single TOML value, while toml::Table represents a TOML table.

Q: Can I use the toml crate with other Rust libraries?

A: Yes, the toml crate is designed to work with other Rust libraries.

To install the toml crate, add the following line to your Cargo.toml file:

[dependencies]
toml = "0.5.8"

AI agent tools available. The CodeTidy MCP Server gives Claude, Cursor, and other AI agents access to 60+ developer tools. One command: npx @codetidy/mcp