BioErrorLog Tech Blog

試行錯誤の記録

clapのmin_valuesやtakes_valueがnot found | Rust

Rustのコマンドラインパーサーclapにて、Argmin_values()takes_value()が見当たらずにnot foundエラーになったので備忘録。

はじめに

Rustの練習帳というオライリー本のサンプルコードが最近のclapのバージョンだと動かなかったのをきっかけに、clapのAPI変遷を調べたのでその記録メモです。

# 作業バージョン
clap = "4.5.16"

clapのmin_valuesやtakes_valueが見当たらない

問題

問題のコードはこちら:

use clap::{Arg, Command};

fn main() {
    let matchs = Command::new("echors")
        .version("0.1.0")
        .author("BioErrorLog")
        .about("Rust echo")
        .arg(
            Arg::new("text")
                .value_name("TEXT")
                .help("Input text")
                .required(true)
                .min_value(1),
        )
        .arg(
            Arg::new("omit_newline")
                .short('n')
                .help("Do not print newline")
                .takes_value(false),
        )
        .get_matches();

    println!("{:#?}", matchs)
}

こちらを実行する下記のようなエラーが発生します。

error[E0599]: no method named `min_value` found for struct `Arg` in the current scope
  --> src/main.rs:13:18
   |
9  | /             Arg::new("text")
10 | |                 .value_name("TEXT")
11 | |                 .help("Input text")
12 | |                 .required(true)
13 | |                 .min_value(1),
   | |                 -^^^^^^^^^ method not found in `Arg`
   | |_________________|
   |

error[E0599]: no method named `takes_value` found for struct `Arg` in the current scope
  --> src/main.rs:19:18
   |
16 | /             Arg::new("omit_newline")
17 | |                 .short('n')
18 | |                 .help("Do not print newline")
19 | |                 .takes_value(false),
   | |                 -^^^^^^^^^^^ method not found in `Arg`
   | |_________________|
   |

For more information about this error, try `rustc --explain E0599`.
error: could not compile `echors` (bin "echors") due to 2 previous errors

Argmin_values()takes_value()に関するシンプルなnot foundエラーですね。

解決策

代わりにnum_args()を使います。

num_args()に範囲指定を渡すことで、引数の数の条件を設定できます。

use clap::{Arg, Command};

fn main() {
    let matchs = Command::new("echors")
        .version("0.1.0")
        .author("BioErrorLog")
        .about("Rust echo")
        .arg(
            Arg::new("text")
                .value_name("TEXT")
                .help("Input text")
                .required(true)
                .num_args(1..),  // 引数1個以上
        )
        .arg(
            Arg::new("omit_newline")
                .short('n')
                .help("Do not print newline")
                .num_args(0),  // 引数なし
        )
        .get_matches();

    println!("{:#?}", matchs)
}

num_args(0)のように引数の数を直接指定することもできますし、num_args(1..)のように範囲を指定することも可能です。

範囲はRustでの通常の範囲構文と同じく、

  • <start>..: <start>以上
  • ..<end>: <end>未満 (排他)
  • <start>..<end>: <start>以上<end>未満 (排他)
  • <start>..=<end>: <start>以上<end>以下 (含有)

のように書きます。

背景

CHANGELOGを覗いてみると、clapのversion 4.0.0にて変更が入ったことがわかります。

元々clapには引数を指定するための方法が複数存在していました。

  • Arg::multiple_values(true)
  • Arg::number_of_values(4)
  • Arg::min_values(2)
  • Arg::max_values(20)
  • Arg::takes_value(true)

4.0.0からは、これら引数の数を指定する場合にはnum_args()一本で済むようになった、ということです。 確かにこっちの方がシンプルで良いですね。

この辺りの背景議論は下記のissueで確認することができます。

Simplify the `takes_value` API (range-based `takes_values`) · Issue #2688 · clap-rs/clap · GitHub

おわりに

以上、clapのtakes_value系APIがnot foundエラーになる問題の対処法メモでした。

どなたかの参考になれば幸いです。

[関連記事]

www.bioerrorlog.work

参考

clap/CHANGELOG.md at master · clap-rs/clap · GitHub

Simplify the `takes_value` API (range-based `takes_values`) · Issue #2688 · clap-rs/clap · GitHub

Arg in clap::builder - Rust

ValueRange in clap::builder - Rust

Range in std::ops - Rust

O'Reilly Japan - Rustの練習帳