🦀

Rust 小ネタ: `clap::Parser` をモジュール内に隠蔽する

2024/06/28に公開

こんにちは。Fairy Devices株式会社 となんらかの関わりがある nogiro (Twitter (現 Twitter): @nogiro_iota) です。Fairy Devices の Slack にある、Rust 雑談チャンネルで話に出た小ネタを共有します。

clap で derive 機能を使ってコマンドライン引数を解析するとき、clap::Parser::parse() を呼ぶファイルで use clap::Parser as _; などして、トレイトをインポートする必要があります [1]fn main()use しがちですが、引数解析のための型を定義しているモジュールで、ラップした関数を提供しているクレートを見かけてかしこいなと思ったので共有します。

すごく嬉しいわけではないけどちょっと嬉しい、という小ネタです。

やりがちな実装

Rust でコマンドラインライン引数を解析するとき、以下のように引数解析用の型 (Args) に clap::Parser を derive して、main 関数で <Args as clap::Parser>::parse() を呼ぶ、という実装をやりがちです。

src/args.rs
use clap::{ArgAction, Parser};

#[derive(Parser, Debug)]
pub struct Args {
    #[arg(short, long, action = ArgAction::Count)]
    pub verbose: u8,
}
src/main.rs
mod args;

use clap::Parser as _;
use crate::args::Args;

fn main() {
    let args = Args::parse();
    println!("args = {args:?}");

    todo!()
}
以下を実行して、(vim のところで) 上記のファイルを書けば試せるはずです
cargo new --name clap-capsule ~/tmp/rust-sandbox-clap-capsule
cd "$_"
cargo add --features clap/derive clap
vim src/args.rs src/main.rs
cargo run -- -vv

隠蔽する

Args に以下のようなメソッドを実装します。

src/args.rs
// ...

impl Args {
    pub fn parse() -> Self {
        <Self as Parser>::parse()
    }
}

そうすると main.rs は以下のように書き換えることができて、clap::Parser への依存を隠すことができます。

src/main.rs
mod args;

use crate::args::Args;

fn main() {
    let args = Args::parse();
    println!("args = {args:?}");

    todo!()
}

なにが嬉しいのか?

いろいろなところでバラバラにインポートしなくて良くなるので、少しだけ凝集度が上がって読みやすくなります。

また、clap::Parser 以外の他のトレイトに適用すると、同じような効果が得られることが期待されます。

終わり。

脚注
  1. use する代わりに フルパス記法<ArgsType as clap::Parser>::parse() としても良いですが、この記事は clap::Parser を呼ぶ側が必要とすることを隠蔽することでなくしたいという話です。 ↩︎

フェアリーデバイセズ公式

Discussion