The csv library for working with CSV files in Rust
CSV (Comma-Separated Values) is one of the most common formats for storing tabular data. In Rust, there is a powerful, flexible, and performant library csv for working with CSV, which is part of the BurntSushi ecosystem (author of ripgrep and regex). It allows you to read, write, serialize, and deserialize data with minimal effort and maximum type safety.
Installation
Add the dependency to Cargo.toml:
[dependencies]csv = "1.3"For serialization/deserialization of structs, you will also need serde:
[dependencies]csv = "1.3"serde = { version = "1.0", features = ["derive"] }Main features of the library
- Reading and writing with configurable delimiters, quotes, and escape characters.
- Automatic header handling — working by column names.
- Serialization/Deserialization via
serde— converting CSV rows into strongly typed Rust structs. - Flexible error handling — error positioning (line number, column).
- Streaming processing — working with large files without loading everything into memory.
- Support for non-UTF8 encodings (via external crates, e.g.,
encoding).
Reading a CSV file
The simplest way to read CSV is to use Reader and an iterator over records.
Example 1: Reading without headers
use csv::ReaderBuilder;use std::error::Error;
fn read_csv_no_headers() -> Result<(), Box<dyn Error>> { let data = "name,age,cityAlice,30,LondonBob,25,Paris"; let mut reader = ReaderBuilder::new() .has_headers(false) .from_reader(data.as_bytes());
for result in reader.records() { let record = result?; println!("{:?}", record); } Ok(())}Example 2: Reading with headers
use csv::Reader;use std::error::Error;
fn read_csv_with_headers() -> Result<(), Box<dyn Error>> { let data = "name,age,cityAlice,30,LondonBob,25,Paris"; let mut reader = Reader::from_reader(data.as_bytes());
for result in reader.deserialize() { let record: (String, u32, String) = result?; println!("{} is {} years old from {}", record.0, record.1, record.2); } Ok(())}Using Serde for strong typing
The most convenient way is to define a struct annotated with #[derive(Deserialize)].
use serde::Deserialize;use csv::Reader;use std::error::Error;
#[derive(Debug, Deserialize)]struct Person { name: String, age: u32, city: String,}
fn read_typed_csv() -> Result<(), Box<dyn Error>> { let data = "name,age,cityAlice,30,LondonBob,25,Paris"; let mut reader = Reader::from_reader(data.as_bytes());
for result in reader.deserialize() { let person: Person = result?; println!("{:?}", person); } Ok(())}This guarantees that the data will be automatically cast to the required types. If, for example, the age field contains the string "abc", the program will return an error indicating the line and column.
Writing a CSV file
The Writer is used for writing. You can write both raw strings and serialized structs.
Example: writing via a struct
use serde::Serialize;use csv::Writer;use std::error::Error;
#[derive(Debug, Serialize)]struct Person { name: String, age: u32, city: String,}
fn write_csv() -> Result<(), Box<dyn