Yomiage
メニュー

Rust実践講座 17:総合演習・タスクの不変条件を型へ集める

更新 2026/9/27 6:22:11 公開

投稿者: しゅりりん

ここから保存できるタスク管理CLIを作ります。前提は第01〜16回です。Java・Python編と同じadd、list、doneを、RustのResultと所有権で実装します。

契約を先に決める

IDは正のu64、題名は前後の空白を除いた1〜200 Unicodeスカラー値、完了状態はboolです。改行とタブは認めません。未完了を完了にし、すでに完了でも成功します。存在しないIDはエラー、破損ファイルは読み込み失敗とし、空として上書きしません。同時更新は対象外です。

cargo new study-tasks-rs --edition 2024 で作り、src/task.rsを追加します。Serdeなどの依存設定とmainは次回でそろいます。

use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub(crate) struct Task {
    pub(crate) id: u64,
    pub(crate) title: String,
    pub(crate) done: bool,
}

impl Task {
    pub(crate) fn new(id: u64, title: &str) -> Result<Self, &'static str> {
        let task = Self { id, title: title.trim().to_owned(), done: false };
        task.validate()?;
        Ok(task)
    }

    pub(crate) fn validate(&self) -> Result<(), &'static str> {
        if self.id == 0 { return Err("IDは正の整数です"); }
        if self.title.trim() != self.title || self.title.is_empty()
            || self.title.chars().count() > 200
            || self.title.contains(['\t', '\n', '\r']) {
            return Err("題名は前後空白・改行・タブなしの1〜200文字です");
        }
        Ok(())
    }

    pub(crate) fn complete(&mut self) {
        self.done = true;
    }
}

newで正規化してから検査します。JSONから直接復元した場合はnewが呼ばれないため、復元後にvalidateを呼ぶ必要があります。「コンストラクタに検査を書いたからデシリアライズも安全」という思い込みを避けます。

公開範囲と所有権

この小さなアプリではクレート内の保存・表示処理へフィールドを公開しています。外部へ配布するライブラリなら、フィールドをprivateにして取得メソッドや専用の変換型を用意する設計もあります。

completeは可変参照を受け取ります。呼び出し側は対象タスクを独占して借用し、その範囲でだけ状態を変えます。Java・Python編の不変な値を返す設計との違いを比べてみてください。どちらも、変更箇所が読み取れることが大切です。

練習と解答

練習:IDの最大値へ1を足すとき、普通の加算で十分でしょうか。

解答:checked_addを使って範囲外をResultへ変換します。ビルド設定によって挙動が違う桁あふれに任せず、IDを発行できないことを明示します。

公式資料

Serdeのコンテナ属性とメソッドが参照先です。


Rust全20回の目次 | 前の回 | 次の回

このブラウザでは Web Speech API が利用できません。

1 / 16 · 停止中