🦀

[写経]Rustの練習帳 Chapter 2(その2) 終了コードのお作法

に公開

教材

書籍「Rustの練習帳」を使って、教養としての Rust を勉強。

https://www.oreilly.co.jp/books/9784814400584/

App構造体はどうなった?

clap v4 では App構造体は廃止されたのかを Claude Code くんに質問。

なるほどね。

ということで、書籍のサンプルコードを Command に置き換えて、改めて写経してみる。

use clap::Command;

fn main() {
    let _matches = Command::new("echor")
        .version("0.1.0")
        .author("Ken Youens-Clark <kyclark@gmail.com>")
        .about("Rust echo")
        .get_matches();
}

実行したところ、書籍と異なり、--helpの出力結果に作者とバージョン情報が含まれなくなってしまった。

$ echor --help
Rust echo

Usage: echor

Options:
  -h, --help     Print help
  -V, --version  Print version

バージョンは--version オプションで出力できるが、作者を出力する方法が不明。
設定はできるのに出力できなくなった、なんてことはセキュリティ観点であってもさすがにないよなあ🤔

ということで Command 構造体のリファレンスを確認したところ、 author を表示するには help_template をカスタマイズする必要あり、とのことだった。

注意書きになっているくらいだから、きっとここもv2から仕様が変わったのだろう。

help_template で出力項目を変更したところ、うまくいった🎉

    let _matches = Command::new("echor")
        .version("0.1.0")
        .author("Ken Youens-Clark <kyclark@gmail.com>")
        .about("Rust echo")
        .help_template("{name} ({version}) - {author}")
        .get_matches();
$ echor --help
echor (0.1.0) - Ken Youens-Clark <kyclark@gmail.com>

Arg構造体

書籍のサンプルコードにあった、takes_value や min_values は、v4 の Arg構造体には用意されていない。
Claude Code くんにこのあたりの後継を聞いてみた。

  ┌─────────────────────────────────────────┬──────────────────────────────────────────────────────┐
  │               旧 (v2/v3)                │                       新 (v4)                        │
  ├─────────────────────────────────────────┼──────────────────────────────────────────────────────┤
  │ takes_value(false) (値を取らないフラグ) │ action(ArgAction::SetTrue) (または SetFalse / Count) │
  ├─────────────────────────────────────────┼──────────────────────────────────────────────────────┤
  │ takes_value(true) (値を取る)            │ action(ArgAction::Set) (または Append)               │
  └─────────────────────────────────────────┴──────────────────────────────────────────────────────┘
  ┌─────────────────────────────┬─────────────────┐
  │             旧              │       新        │
  ├─────────────────────────────┼─────────────────┤
  │ min_values(1)               │ num_args(1..)   │
  ├─────────────────────────────┼─────────────────┤
  │ max_values(3)               │ num_args(..=3)  │
  ├─────────────────────────────┼─────────────────┤
  │ min_values(1).max_values(3) │ num_args(1..=3) │
  ├─────────────────────────────┼─────────────────┤
  │ number_of_values(2)         │ num_args(2)     │
  └─────────────────────────────┴─────────────────┘

旧来の min_values のほうがシンプルでわかりやすい気もしたのだけど、範囲指定をしたい時に min_values と max_values の組み合わせが冗長だから num_args に統一したのだろうかね。

v4 向けに修正したコードは以下の通り。
なお、前述の help_template を適用したままだと、出力結果が変わってきてしまうので、外すことにした。

use clap::{Arg, ArgAction, Command};

fn main() {
    let matches = Command::new("echor")
        .version("0.1.0")
        .author("Ken Youens-Clark <kyclark@gmail.com>")
        .about("Rust echo")
        .arg(
            Arg::new("text")
                .value_name("TEXT")
                .help("Input text")
                .required(true)
                .num_args(1..),
        )
        .arg(
            Arg::new("omit_newline")
                .short('n')
                .help("Do not print newline")
                .action(ArgAction::SetTrue),
        )
        .get_matches();

    println!("{:#?}", matches);
}

--help の実行結果も、追加オプションに関する出力が増えたので大丈夫。

$ echor --help
Rust echo

Usage: echor [OPTIONS] <TEXT>...

Arguments:
  <TEXT>...  Input text

Options:
  -n             Do not print newline
  -h, --help     Print help
  -V, --version  Print version

引数無しで実行すると、必須の引数に関する説明が出力されるようになった。

error: the following required arguments were not provided:
  <TEXT>...

ここで新たな疑問が。
書籍だと引数無し実行時の終了コード($?)は 1 が返されていたけれど、手元だと 2 が返ってきている。
この微妙な違いは、終了コードの使い分けが賢くなったからだろうか?

ということで、Claude Codeくんを頼る。

  ┌────────────┬───────────────────────────────────────────────┐
  │ 終了コード │                 慣例的な意味                  │
  ├────────────┼───────────────────────────────────────────────┤
  │ 0          │ 成功                                          │
  ├────────────┼───────────────────────────────────────────────┤
  │ 1          │ 一般的なエラー (プログラム実行中の失敗)       │
  ├────────────┼───────────────────────────────────────────────┤
  │ 2          │ 使い方の誤り (引数不足・不正なオプションなど) │
  └────────────┴───────────────────────────────────────────────┘

ふむ、なるほど😄
引数不足だと 1 ではなくて 2 を返すのが POSIX での慣例、ということすらも知らなかったので、これは良い勉強になった📝

Discussion