03 / 10 · 15 分钟
用 enum 表示天气
漏掉一种天气,程序编不过。
你的项目里会动到:src/model.rs
这一章不新建文件,只改已经有的 src/model.rs。lib.rs 不用动。
天气如果是 String,日志里会同时出现「晴」「晴天」和「sunny」。用 enum 把合法的值定死。地形另写一个 enum:雨可以下在山脊上,也可以下在林子里。
pub enum Weather {
Clear,
Fog,
Rain,
Storm,
Snow,
}
pub enum Terrain {
Trail,
Ridge,
Scree,
Forest,
Camp,
}然后给 DayLog 加上 weather 和 terrain 两个字段。样例里没有雪,match 仍然要写出 Snow。写了 _ 的话,以后新加的天气会静静变成一个错误的词。
在原有字段上补天气和地形。仍然没有 serde。
/// 一天的记录。距离用米保存,公里只在显示时换算。
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DayLog {
/// `YYYY-MM-DD`。
pub date: String,
pub title: String,
pub distance_m: u32,
pub minutes: u32,
pub elevation_m: u32,
pub weather: Weather,
pub terrain: Terrain,
pub note: String,
}动手
给每种天气一个中文
用 match 返回「晴」「雾」「雨」「暴风雨」「雪」。不要写 _ 分支。
pub fn weather_label(weather: Weather) -> &'static str {
todo!("weather_label")
}五种分支,每种一个字符串字面量。返回 &'static str,这些词和程序活得一样久。
terrain_label 是同一写法,用来对照。鞍部那天的天气是「暴风雨」,地形是「碎石坡」。weather_label 可以先留成 todo!()。下面是这一章结束时的 model.rs,测试也在里面。
/// 天气是有限的几种,不是随意的字符串。
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Weather {
Clear,
Fog,
Rain,
Storm,
Snow,
}
/// 脚下是什么。和天气分开,因为雨可以下在山脊上,也可以下在林子里。
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Terrain {
Trail,
Ridge,
Scree,
Forest,
Camp,
}
// region: weather_label
/// 简报里的天气用词。`match` 不写 `_`,漏掉一种天气就编不过。
pub fn weather_label(weather: Weather) -> &'static str {
match weather {
Weather::Clear => "晴",
Weather::Fog => "雾",
Weather::Rain => "雨",
Weather::Storm => "暴风雨",
Weather::Snow => "雪",
}
}
// endregion: weather_label
pub fn terrain_label(terrain: Terrain) -> &'static str {
match terrain {
Terrain::Trail => "小径",
Terrain::Ridge => "山脊",
Terrain::Scree => "碎石坡",
Terrain::Forest => "林地",
Terrain::Camp => "营地",
}
}
// region: day_log
/// 一天的记录。距离用米保存,公里只在显示时换算。
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DayLog {
/// `YYYY-MM-DD`。
pub date: String,
pub title: String,
pub distance_m: u32,
pub minutes: u32,
pub elevation_m: u32,
pub weather: Weather,
pub terrain: Terrain,
pub note: String,
}
// endregion: day_log
#[cfg(test)]
mod tests {
use super::*;
fn meadow() -> DayLog {
DayLog {
date: "2026-09-18".to_string(),
title: "北口草甸".to_string(),
distance_m: 8400,
minutes: 140,
elevation_m: 320,
weather: Weather::Clear,
terrain: Terrain::Trail,
note: "雾停在谷底。".to_string(),
}
}
#[test]
fn meadow_day_keeps_meters() {
let day = meadow();
assert_eq!(day.distance_m, 8400);
assert_eq!(weather_label(day.weather), "晴");
}
#[test]
fn weather_labels_cover_every_variant() {
assert_eq!(weather_label(Weather::Clear), "晴");
assert_eq!(weather_label(Weather::Fog), "雾");
assert_eq!(weather_label(Weather::Rain), "雨");
assert_eq!(weather_label(Weather::Storm), "暴风雨");
assert_eq!(weather_label(Weather::Snow), "雪");
}
}检查点
cargo test model::tests::weather_labels_cover_every_variant
五种天气都有断言。少写一种变体,先是编译失败,不是测试失败。