Back to Blog

Rust CSV Tutorial | Read, Transform & Write CSV Files with Serde | Rust by Examples #11

Sandy LaneSandy Lane

Video: Rust CSV Tutorial | Read, Transform & Write CSV Files with Serde | Rust by Examples #11 by Taught by Celeste AI - AI Coding Coach

Watch full page →

Rust CSV Tutorial: Read, Transform & Write CSV Files with Serde

In this tutorial, you'll learn how to process CSV files in Rust by building a complete employee bonus calculator. We'll read employee data from a CSV file, calculate bonuses based on years of experience, and write the results to a new CSV file using the powerful csv crate and Serde's serialization features.

Code

use std::error::Error;
use std::fs::File;
use serde::{Deserialize, Serialize};

#[derive(Debug, Deserialize)]
struct Employee {
  name: String,
  department: String,
  salary: f64,
  years_experience: u32,
}

#[derive(Debug, Serialize)]
struct BonusReport {
  name: String,
  department: String,
  salary: f64,
  bonus: f64,
  total: f64,
}

fn calculate_bonus(years: u32, salary: f64) -> f64 {
  let bonus_rate = 0.10 * (years as f64);
  let capped_rate = bonus_rate.min(0.50); // cap bonus at 50%
  salary * capped_rate
}

fn main() -> Result<(), Box<dyn Error>> {
  // Open the CSV file with employee data
  let file = File::open("employees.csv")?;
  let mut rdr = csv::Reader::from_reader(file);

  // Read and deserialize employees into a vector
  let employees: Vec<Employee> = rdr.deserialize()
    .filter_map(Result::ok)
    .collect();

  // Transform employees into bonus reports
  let reports: Vec<BonusReport> = employees.iter()
    .map(|e| {
      let bonus = calculate_bonus(e.years_experience, e.salary);
      BonusReport {
        name: e.name.clone(),
        department: e.department.clone(),
        salary: e.salary,
        bonus,
        total: e.salary + bonus,
      }
    })
    .collect();

  // Write the bonus report to a new CSV file
  let out_file = File::create("bonus_report.csv")?;
  let mut wtr = csv::Writer::from_writer(out_file);

  for report in &reports {
    wtr.serialize(report)?;
  }
  wtr.flush()?;

  // Print formatted report to console
  println!("{:20} {:15} {:10} {:10} {:10}", "Name", "Department", "Salary", "Bonus", "Total");
  for r in &reports {
    println!("{:20} {:15} {:10.2} {:10.2} {:10.2}",
      r.name, r.department, r.salary, r.bonus, r.total);
  }

  Ok(())
}

Key Points

  • The csv crate provides easy CSV reading and writing with automatic header handling.
  • Serde's Deserialize and Serialize traits let you convert CSV rows directly to Rust structs and back.
  • Using iterators like filter_map, map, and collect helps efficiently transform and process CSV data.
  • Bonus calculation logic can be cleanly separated and applied during data transformation.
  • Writing results back to CSV is as simple as serializing structs with the CSV writer and flushing the output.