功能说明
这段代码用 csv 与 serde 把 CSV 记录反序列化成强类型结构体。表头中的 sku、quantity、price_cents 会自动对应同名字段,数字列被解析成 u32,因此负数或非数字会直接产生错误,而不是悄悄变成字符串。
它适合导入库存、账单、指标导出等结构化数据。相比手动按列下标取值,结构体字段让后续逻辑更清晰,也能借助编译器发现字段拼写问题。
关键代码
InventoryRow 派生 Deserialize,字段名与 CSV 表头一致。ReaderBuilder::new().trim(csv::Trim::All) 去掉字段两侧空白,from_reader(io::Cursor::new(csv_data)) 让内存字符串实现 Read。
reader.deserialize() 逐条产出 Result<InventoryRow, csv::Error>,record? 把错误向上传播;只要有一行失败,整个函数返回错误,不会返回部分结果。main 断言两行数据、第一行内容,以及负数数量会失败。
使用注意
需要添加 csv 和 serde(启用 derive)依赖。表头必须与字段名完全匹配,或使用 #[serde(rename = "...")] 映射;多余列默认会被忽略,缺失列会报错。u32 不接受负数和小数,金额应统一用整数分。
当前实现一旦遇到坏行就整体失败;如果希望跳过坏行并收集错误,应在循环中记录行号和错误后继续。CSV 中的引号、换行由库处理,但超大文件应直接使用文件 reader,而不是先读入字符串。
验证方式
示例数据两行,解析后长度为 2,第一行等于 InventoryRow { sku: "A-1", quantity: 5, price_cents: 1200 }。把数量改成 -5 后 parse_inventory 返回错误,断言通过。可以再加入带引号的逗号字段和缺失列,观察 Serde 的错误信息。
代码示例
use serde::Deserialize;
use std::error::Error;
use std::io;
#[derive(Debug, Deserialize, PartialEq)]
struct InventoryRow {
sku: String,
quantity: u32,
price_cents: u32,
}
fn parse_inventory(csv_data: &str) -> Result<Vec<InventoryRow>, Box<dyn Error>> {
let mut reader = csv::ReaderBuilder::new()
.trim(csv::Trim::All)
.from_reader(io::Cursor::new(csv_data));
let mut rows = Vec::new();
for record in reader.deserialize() {
let row: InventoryRow = record?;
rows.push(row);
}
Ok(rows)
}
fn main() -> Result<(), Box<dyn Error>> {
let data = "sku,quantity,price_cents\nA-1,5,1200\nB-2,3,800\n";
let rows = parse_inventory(data)?;
println!("{} rows, first={:?}", rows.len(), rows.first());
assert_eq!(rows.len(), 2);
assert_eq!(
rows[0],
InventoryRow {
sku: "A-1".into(),
quantity: 5,
price_cents: 1200,
}
);
let bad = "sku,quantity,price_cents\nA-1,-5,1200\n";
assert!(parse_inventory(bad).is_err(), "negative quantity must fail");
Ok(())
}