Working with CSV in Rust: the csv library and practical examples
The CSV (Comma-Separated Values) format remains one of the most popular ways to store tabular data. In Rust, there is a mature and high-performance csv library for working with CSV, which provides a convenient API for reading, writing, and processing data. In this article, we will take a detailed look at how to work efficiently with CSV in Rust.
Installation and adding the library
To start, add the dependency to your Cargo.toml file:
[dependencies]csv = "1.3"The csv library supports working with encodings, customizable delimiters and quotes, and also provides the ability to serialize/deserialize data via serde traits.
Reading a CSV file
The simplest way to read a CSV is to use csv::Reader. Consider an example with a users.csv file:
use std::error::Error;use csv::ReaderBuilder;
fn main() -> Result<(), Box<dyn Error>> { // Create a reader with settings let mut rdr = ReaderBuilder::new() .has_headers(true) // first row is headers .delimiter(b',') // delimiter is comma .from_path("users.csv")?;
// Print headers let headers = rdr.headers()?; println!("Headers: {:?}", headers);
// Iterate over records for result in rdr.records() { let record = result?; println!("{:?}", record); } Ok(())}Processing records with deserialization
For working with structured data, it is convenient to use deserialization into custom structs. Add serde and serde_derive:
[dependencies]csv = "1.3"serde = { version = "1.0", features = ["derive"] }Now you can define a struct and read data directly:
use serde::Deserialize;
#[derive(Debug, Deserialize)]struct User { name: String, age: u8, email: String,}
fn main() -> Result<(), Box<dyn std::error::Error>> { let mut rdr = csv::Reader::from_path("users.csv")?;
for result in rdr.deserialize() { let user: User = result?; println!("User: {} ({} years old) - {}", user.name, user.age, user.email); } Ok(())}Writing data to CSV
For writing, csv::Writer is used. Example of writing structured data:
use csv::Writer;use serde::Serialize;
#[derive(Debug, Serialize)]struct Product { id: u32, name: String, price: f64, in_stock: bool,}
fn main() -> Result<(), Box<dyn std::error::Error>> { let mut wtr = Writer::from_path("products.csv")?;
// Write headers wtr.write_record(&["id", "name", "price", "in_stock"])?;
// Write data via serialization let products = vec![ Product { id: 1, name: "Laptop".into(), price: 75000.50, in_stock: true }, Product { id: 2, name: "Mouse".into(), price: 1500.00, in_stock: false }, ];
for product in &products { wtr.serialize(product)?; }
wtr.flush()?; println!("Data written to products.csv"); Ok(())}Configuring delimiters and quotes
The library allows flexible format configuration. For example, for TSV (tab as delimiter):
let mut rdr = csv::ReaderBuilder::new() .delimiter(b'\\t') .flexible(true) // allow varying number of fields .quoting(false) // disable quotes .from_reader(io::stdin());Error handling and large files
When working with large CSV files, it is important to consider performance. csv uses buffered reading and does not load