cargoのソースを読んでみよう - 第2回 : main関数

# 三行

- cargoは*env_logger*と*docopt*ってcrateを使ってるよ。
- Rustの統一されたエラー処理の初歩が分かるよ。
- イテレータアダプタも面白いよ。


# `main()`関数

前回、cargoの`main()`関数が`src/bin/cargo.rs`にあることを突き止めました:

```sh-session:
% cargo metadata --no-deps | jq '.packages[].targets[] | select(.kind==["bin"])'
{
  "kind": [
    "bin"
  ],
  "name": "cargo",
  "src_path": "src/bin/cargo.rs"
}
```


というわけで、ようやく`main()`関数にたどり着くことができました:

```rust:src/bin/cargo.rs(抜粋)
fn main() {
    env_logger::init().unwrap();
    execute_main_without_stdin(execute, true, USAGE)
}
```


# *env_logger* crate

まず、一行目の`env_logger::init()`関数の呼び出しが目に留まります。Racer for emacsを使っていれば、この`init`のところにカーソルを合わせてM-.を押すと、このソースに飛ぶことができます。私の環境では`~/.cargo/registry/src/github.com-1ecc6299db9ec823/env_logger-0.3.5/src/lib.rs`というファイルに飛びました。

*env_logger* crateの`Cargo.toml`を読むと、これはRustプロジェクトが開発しているlogライブラリの一部であることが想像できます:

```env_logger-0.3.5/Cargo.toml(抜粋)
[package]
name = "env_logger"
version = "0.3.5"
authors = ["The Rust Project Developers"]
license = "MIT/Apache-2.0"
repository = "https://github.com/rust-lang/log"
documentation = "http://doc.rust-lang.org/log/env_logger"
homepage = "https://github.com/rust-lang/log"
```

この*env_logger*のリポジトリを覗いてみます。なお、上記`repository`のURLは既に無効で、現在のリポジトリは<https://github.com/rust-lang-nursery/log>です[^rust-lang-nursery]。[https://github.com/rust-lang-nursery/log/README.md](README)を読むと、次のようなことがわかります:

[^rust-lang-nursery]: rust-lang-nurseryへの移動の経緯については[RFC1242](https://github.com/rust-lang/rfcs/blob/master/text/1242-rust-lang-crates.md)を参照。

- logライブラリは`log` crateと`env_logger` crateから成る。
- `log` crateは、ログを生成する側へのfacadeを提供する。
- `env_logger` crateは、ログを実際に標準エラー出力などに吐き出すバックエンドを提供する。

このように二つの層に分けることによって、ログのバックエンドを自由に差し替えられるように設計されています。*env_logger* crateは一番シンプルなバックエンド実装を提供しています。

ついでに、軽く`log` crateの方にも触れておきます。cargoのソースを読んでいると、`debug!` やら `error!` やら `warn!` やらといったマクロに出会います。これが`log` crateの提供しているログ出力関数ですので覚えておくとよいでしょう。


## `env_logger::init().unwrap()`

`env_logger`の素性が分かったので、`main()`関数に戻って`env_logger::init().unwrap()`という行について吟味してみましょう。[`env_logger::init()`関数のリファレンス](http://rust-lang-nursery.github.io/log/env_logger/fn.init.html)を見ると、次のようなインターフェースであることが分かります:

```rust:
pub fn init() -> Result<(), SetLoggerError>
```

これは興味深いイディオムですね。つまり、正味の戻り値がない関数の場合でも、エラーを単体で返すのではなく、`Result`型でくるんで返すことにより、統一的なエラーハンドリングができるというわけです。ここでは単純に`unwrap()`を呼び出していますが、Rustの`Result`型の`unwrap()`関数は、内部で`panic!`マクロを呼んでいるため、安全に終了することができるようになっています[^panic]。

[^panic]: panicするのは気持ち悪いような気もしますが、この関数が失敗するようなケースでは、どのみちまともにリカバリできる気もしないので、わざわざ自前の関数を用意して真面目にエラーハンドリングをする必要もないのでしょう。いずれにしても、余計なコードパスを通らずに安全に終了することだけは保証されます。

`unwrap()`を忘れると大変なことになりそうな気も、と思うかもしれませんが、`Result`型の定義は次のようになっています:

```rust:libcore/result.rs
#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
#[must_use]
#[stable(feature = "rust1", since = "1.0.0")]
pub enum Result<T, E> {
...
```

この`#[must_use]`アトリビュートのおかげで、結果を一切使わずに捨ててしまうとコンパイラが次のような警告を発します:

```sh-session:
warning: unused result which must be used, #[warn(unused_must_use)] on by default
  --> src/bin/cargo.rs:70:5
   |
70 |     env_logger::init();
   |     ^^^^^^^^^^^^^^^^^^^

```


なお、関数の名前から明らかなように、この`env_logger::init()`関数は*env_logger* crateの初期化を行います:

> Initializes the global logger with an env logger.
>
> This should be called early in the execution of a Rust program, and the global logger may only be initialized once. Future initialization attempts will return an error.


# `execute_main_without_stdin()`関数

`main()`関数の次の行は`execute_main_without_stdin()`関数の呼び出しです。[`execute_main_without_stdin()`関数は`src/cargo/lib.rs`で実装されています(GitHub)](https://github.com/rust-lang/cargo/blob/rust-1.15.1/src/cargo/lib.rs#L51)。コードを眺めてみると、入れ子構造で若干分かりにくいものの、次のような構造になっています:

```
execute_main_without_stdin()
  |
  +--- process()
         |
         +--- ||()  // 無名関数
                |
                +--- call_main_without_stdin()
                       |
                       +--- exec()   // execクロージャとしてsrc/bin/cargo.rsのexecute()関数が渡されている
                       
```

これらは、親プロセスから受け渡された引数や環境変数に基づいて、cargoの実行に必要となる基本的なコンフィグレーション情報を構築するルーチンのようです。このように入れ子にするスタイルは、C言語などに慣れているとちょっと奇異な感じもしますが、Rustではジェネリクスなどの豊富な文脈を用いることにより自然に書くことができるため、このように書くとスコープに基づいたライフタイムとの相性が良いのだと思います[^recursive]。

[^recursive]: とはいえ、さすがにこれはちょっと迂遠すぎる書き方だったようで、Rust 1.19.0のcargoでは[こんなコード(GitHub)](https://github.com/rust-lang/cargo/blob/rust-1.19.0/src/cargo/lib.rs#L108)になっており、はるかに普通の書き方になっています。

ところで、`execute_main_without_stdin()`関数はジェネリック関数となっています。`exec`引数は実際のサブコマンドの処理を行うクロージャですが、このクロージャの第一引数の型Tと、正味の戻り値の型Vについては**我関せず**ということを表明しています。`execute_main_without_stdin()`関数が再利用されるとも考えにくいので、つまりこれは、再利用のための柔軟性を与えるためにジェネリクスを使用しているのではなく、**型の詳細について、自分の責任範囲と関係ない部分については関心を持たない**ということを表明するためにジェネリクスを使用しているのだと思います。Rustではトレイトによって必要最低限の型制約を与えることができるため、C++のテンプレートのように実体化するまで何が起こるか分からないことに起因する欠点[^c++-template-disadvantage]がほとんどなく、このように分離することで純粋な構造だけに注力できるようになるため、これは良いスタイルなのではないでしょうか。

[^c++-template-disadvantage]: 要するに、訳の分からないエラーメッセージが大量に出るアレのことです。コンセプトが入るとだいぶマシになりそう。


# `process()`関数

次に`process()`関数を見てみましょう。

```rust:src/cargo/lib.rs(抜粋)
fn process<V, F>(mut callback: F)
    where F: FnMut(&[String], &Config) -> CliResult<Option<V>>,
          V: Encodable
{
    let mut config = None;
    let result = (|| {
        config = Some(Config::default()?);
        let args: Vec<_> = try!(env::args_os().map(|s| {
            s.into_string().map_err(|s| {
                human(format!("invalid unicode in argument: {:?}", s))
            })
        }).collect());
        callback(&args, config.as_ref().unwrap())
    })();
    let mut verbose_shell = shell(Verbose, Auto);
    let mut shell = config.as_ref().map(|s| s.shell());
    let shell = shell.as_mut().map(|s| &mut **s).unwrap_or(&mut verbose_shell);
    process_executed(result, shell)
}
```


## 謎の無名関数

このコードを見て、少し不思議に感じるのは`result`変数への代入のコードです:

```rust:
    let result = (|| {
        config = Some(Config::default()?);
        let args: Vec<_> = try!(env::args_os().map(|s| {
            s.into_string().map_err(|s| {
                human(format!("invalid unicode in argument: {:?}", s))
            })
        }).collect());
        callback(&args, config.as_ref().unwrap())
    })();
```

これは、無名関数を作ってこれを即座に呼び出し、その結果を代入するという形になっています。Rustでは、ブロックそのものが式になれるため、このように無名関数の呼び出しにする必要はないのではないかという気もしてきます。しかしながら、単なるブロックではなく、一度無名関数でくるんでそれを呼び出している理由は、エラー処理の関係です[^do-catch]。

[^do-catch]: なお、最近のnightlyには`do catch`という構文が入ったようです。

まずは、次の部分です:

```rust:
        config = Some(Config::default()?);
```

この`?`演算子は、次のコードとほぼ等価な[^nearly-equiv]シンタックスシュガーです:

```rust:
        config = Some(
            match Config::default() {
                Ok(v) => v,
                Err(e) => return Err(e),
            }
        );
```

[^nearly-equiv]: 「ほぼ等価」であって「等価」ではありません。コンパイルエラーになります。このあたりの事情は、[例の本のエラーハンドリングの章](https://rust-lang-ja.github.io/the-rust-programming-language-ja/1.6/book/error-handling.html)を**熟読**してください。

つまり、`Config::default()`関数がエラーを返した場合の振る舞いが、無名関数とブロックでは異なるのです。無名関数の場合には`result`変数に`Err(error)`という値が代入され、`process()`関数の残りの部分が実行されますが、ブロックの場合には即座に`process()`関数から脱出しようとしてしまいます。これが、無名関数で包んである理由です。


## `?`演算子と`try!`マクロ

なお、`?`演算子が導入される前から、`try!`マクロを使って次のように書くことができました:

```rust:
        config = Some(try!(Config::default()));
```

このように、マクロでも実現できることではあるのですが、あまりにも使用頻度が多いのと、`try!`マクロの入れ子が発生すると読みにくいコードになるため、言語機能として用意したというわけです。

しかしながら、今のところ`try!`マクロを完全に取り除くこともできていないようで、この次の文は`try!`マクロを使って書かれています:

```rust:
        let args: Vec<_> = try!(env::args_os().map(|s| {
            s.into_string().map_err(|s| {
                human(format!("invalid unicode in argument: {:?}", s))
            })
        }).collect());
```

これは、次のように書き直しても良さそうなものです:

```rust:
        let args: Vec<_> = env::args_os().map(|s| {
            s.into_string().map_err(|s| {
                human(format!("invalid unicode in argument: {:?}", s))
            })
        }).collect()?;
```

ところが、まだ`?`演算子の実装が不十分なのか、これでは型推論に失敗してしまうため、次のように`collect()`関数の方に型パラメータを付けないとコンパイルが通りません:

```rust:
         let args = env::args_os().map(|s| {
            s.into_string().map_err(|s| {
                human(format!("invalid unicode in argument: {:?}", s))
            })
        }).collect::<CargoResult<Vec<_>>>()?;
```

これならば`try!`マクロの方がマシですね。まあ、そのうち直るんじゃないかとは思います。

ところで、私が一つ気になっている部分は、`config`変数を`mut`にして無名関数内で代入し、外に取り出していることです:

```rust:
    let mut config = None;
    let result = (|| {
        config = Some(Config::default()?);
  ...
```


私ならこうするかもしれません[^unless-tuple] [^combinator1] [^combinator2]:

```rust:
    let (result, config) = match (|| {
        let config = Config::default()?;
        let args: Vec<_> = try!(env::args_os().map(|s| {
            s.into_string().map_err(|s| {
                human(format!("invalid unicode in argument: {:?}", s))
            })
        }).collect());
        Ok((callback(&args, &config)?, config))
    })() {
        Ok(s) => (Ok(s.0), Some(s.1)),
        Err(e) => (Err(e), None),
    };
```

[^unless-tuple]: あるいは、`process()`関数の残りの部分を書き直せば、`CliResult<(Option<V>, Config)>>`型の変数一つで持ち回すことが可能かもしれません。が、所有権がらみでだいぶ面倒くさいことになる。

[^combinator1]: `match`の代わりにコンビネータで`.map(|s| (Ok(s.0), Some(s.1))).unwrap_or_else(|s| (Err(s), None))`としてもいいんですが、却って分かりにくいっすね。`match`が中置にできればいいのだが。

[^combinator2]: 無名関数ではなく、全部コンビネータでつないでいくという方法もありうるのですが、`args`を`collect()`するのに型注釈が必要になるせいで冗長になる上に、途中で明示的に`CargoResult<_>`を`CliResult<_>`に変換する`or_else()`を入れないといけなくて、却って長くなります。


## とどのつまり、この無名関数が何をやっているか

エラー処理の話でだいぶ横道にそれた気がするので、元に戻します。`process()`関数の処理の本体はこの無名関数部分で、残りは後処理なので、まずはこの無名関数を詳細に読んでいくことにします:

```rust:
    let result = (|| {
        config = Some(Config::default()?);
        let args: Vec<_> = try!(env::args_os().map(|s| {
            s.into_string().map_err(|s| {
                human(format!("invalid unicode in argument: {:?}", s))
            })
        }).collect());
        callback(&args, config.as_ref().unwrap())
    })();
```

この無名関数は、実行環境から引数や環境変数などを取得して、これを`[String]`型の引数リストと`Config`型のコンフィグレーション情報の二つにまとめあげ、`callback()`関数へと渡すという働きをしています。

先ほど引用したこの部分が、コンフィグレーションのデフォルト値を生成していることは明らかでしょう:

```rust:
        config = Some(Config::default()?);
```

`Config`型や`Config::default()`関数の実装は次回見ることにします。

その次では、このプロセスに渡されたコマンドライン引数のリストをVec<String>に変換しています:

```rust:
        let args: Vec<_> = try!(env::args_os().map(|s| {
            s.into_string().map_err(|s| {
                human(format!("invalid unicode in argument: {:?}", s))
            })
        }).collect());
```

`std` crateの`env::args_os()`関数は`ArgsOs`という型のイテレータを返しますが、このイテレータで巡ることができるのは、要素として`OsString`型の値を保持している線形コンテナです。`OsString`型は実行環境のネイティブな文字コードで保持された文字列で、そのままではRustの標準的な`String`型とは互換性がありません。そのため、各要素を`OsString::into_string()`関数で`String`型へと変換する必要があります。

なお、このようにイテレータに対して`Iterator::map()`などの**イテレータアダプタ**を利用してモナド的な演算パイプラインを構築し、最後に`collect()`関数を適用して別のコンテナを構築するのがRustでは常套手段となっているようです。なお、`collect()`関数で最終的に構築されるコンテナとしては、`FromIterator`トレイトを実装している任意のコンテナを用いることができるため、上記のように型アノテートでそれを指定しないと曖昧になってしまいます[^type-param-for-collect] [^iterator-map-to-result]。この辺の事情は[例の本のイテレータの項](https://rust-lang-ja.github.io/the-rust-programming-language-ja/1.6/book/iterators.html)を参照してください。

[^type-param-for-collect]: あるいは、先ほども書いたとおり、`collect()`関数の型パラメータとして戻り値型を明示的に付ける方法もありますが、このコードのように要素をResult型へマップするようなイテレータアダプタを含んでいる場合には冗長になりがちです。

[^iterator-map-to-result]: イテレータアダプタとして、要素を`Result<T,E>`型へ変換するような`map()`関数が`collect()`関数の直前に挿入されていると、`collect()`関数の戻り値型は`Container<Result<T,E>>`ではなく`Result<Container<T>, E>`型となり、各要素から`Result`型が引き剥がされ、コンテナを`Result`型で包む形となります。`collect()`関数の変換の途中でエラーが発生すると、そこまでの繰り返しの結果は捨てられ、エラー値がそのまま`collect()`関数の結果となります。

最後に、この二つの値`config`と`args`を`callback`へと渡しています:

```rust:
        callback(&args, config.as_ref().unwrap())
```

このコールバックの中で、cargoの正味の処理が行われることになります。`config`は`Option<Config>`型でなおかつSome()であることが確実なので、`as_ref()`関数で`Option<&Config>`へと変換した後、`unwrap()`関数で`&Config`な参照を取り出しています。


## `process()`関数の残りの部分

上の無名関数の呼び出しの結果、`result`変数に格納されているのは`callback`の戻り値か、その呼び出し前に発生したエラーです。この変数の型はcallbackの戻り値型である`CliResult<Option<V>>`となります。

残りの部分は、この`result`変数の内容を最終的に表示するためのコードです:

```rust:
    let mut verbose_shell = shell(Verbose, Auto);
    let mut shell = config.as_ref().map(|s| s.shell());
    let shell = shell.as_mut().map(|s| &mut **s).unwrap_or(&mut verbose_shell);
    process_executed(result, shell)
}

pub fn process_executed<T>(result: CliResult<Option<T>>, shell: &mut MultiShell)
    where T: Encodable
{
    match result {
        Err(e) => handle_error(e, shell),
        Ok(Some(encodable)) => {
            let encoded = json::encode(&encodable).unwrap();
            println!("{}", encoded);
        }
        Ok(None) => {}
    }
}
```

ここで登場している`shell`変数やその型である`MultiShell`は、その名前とは裏腹に、あまりシェルとは関係がなく、標準出力および標準エラー出力へのハンドルと、その属性などを保持しているデータ構造です。コードを書いた人の気持ちとしては、「呼び出し元のシェルによって決定された属性」というような意味合いなのかもしれません。

`callback`の呼び出しの結果がエラーなら、`handle_error()`関数を呼び出してそのエラーの内容を表示し、成功で何か情報を持っていれば、その内容をJSON形式の文字列に変換して表示します。成功で何も情報がなければ、無言のまま終了します。いずれにしても、この関数を抜けた後は`main()`関数の最後に到達し、cargoのプロセス自体が終了することになります。

`handle_error()`関数を読むためには、後回しにしていた`Config`型や`MultiShell`型についての知識が必要になるため、これまた次回にしましょう。


# `call_main_without_stdin()`関数

さて、もう一息。もう一度コールスタックを思い出してみましょう:

```
execute_main_without_stdin()
  |
  +--- process()
         |
         +--- ||()  // 無名関数
                |
                +--- call_main_without_stdin()
                       |
                       +--- exec()   // execクロージャとしてsrc/bin/cargo.rsのexecute()関数が渡されている
                       
```

`execute_main_without_stdin()`関数から呼び出された`process()`関数の`closure`引数として渡されているのは無名関数です:



```rust:src/cargo/lib.rs(抜粋)
pub fn execute_main_without_stdin<T, V>(
                                      exec: fn(T, &Config) -> CliResult<Option<V>>,
                                      options_first: bool,
                                      usage: &str)
    where V: Encodable, T: Decodable
{
    process::<V, _>(|rest, config| {
        call_main_without_stdin(exec, config, usage, rest, options_first)
    });
}
```

[^process-type-param]

[^process-type-param]: ところで、この`process()`関数の呼び出しに明示的に付けられた`::<V, _>`という型パラメータがなぜ必要なのか良く分からないんですが（無くてもコンパイルが通る）、型推論が弱かったころの名残ですかねえ。

ここからさらに呼び出されている`call_main_without_stdin()`関数はすぐ直後で定義されています:

```rust:src/cargo/lib.rs(抜粋)
pub fn call_main_without_stdin<T, V>(
            exec: fn(T, &Config) -> CliResult<Option<V>>,
            config: &Config,
            usage: &str,
            args: &[String],
            options_first: bool) -> CliResult<Option<V>>
    where V: Encodable, T: Decodable
{
    let flags = flags_from_args::<T>(usage, args, options_first)?;
    exec(flags, config)
}
```

この関数は、コマンドライン引数を解析して、その結果を`exec`クロージャに渡しているだけの関数です。たったこれだけなんだから、何で`execute_main_without_stdin()`関数に直接書かないんだ、と思うかもしれませんが、よーくみると`pub`が付いていることが分かります。この関数は別のところからも呼ばれるので、独立した関数として書いてあるんですね。

ここでやっと、コマンドライン引数の処理をしていることが分かります。`flags_from_args()`関数を見てみましょう:

```rust:src/cargo/lib.rs(抜粋)
fn flags_from_args<T>(usage: &str, args: &[String], options_first: bool) -> CliResult<T>
    where T: Decodable
{
    let docopt = Docopt::new(usage).unwrap()
                                   .options_first(options_first)
                                   .argv(args.iter().map(|s| &s[..]))
                                   .help(true);
    docopt.decode().map_err(|e| {
        let code = if e.fatal() {1} else {0};
        CliError::new(human(e.to_string()), code)
    })
}
```

ここでは、*docopt*という外部のcrateを使ってコマンドライン引数処理を行っているようですね。*env_logger*の時と同様にして正体を探ることができますが、その過程は省略します。


## docopt

*docopt*はなかなか面白いライブラリで、usage文字列、すなわち、人間が読める形で書かれたコマンドの使用法からコマンドライン引数プロセッサを生成してくれます[^docopt-original]。

[^docopt-original]: もともとはPythonの同名のパッケージが元になっているようですね。

この段階では[^usage-str]、`usage`変数には次のような文字列が渡されています:

[^usage-str]: 上述した通り、`call_main_without_stdin()`関数は、ここで見ているコードパス以外のところからも呼ばれます。具体的には、`src/bin/cargo.rs`の`execute()`関数でサブコマンドの処理を行う際に呼ばれるのですが、この時にはサブコマンドごとに固有のusageが渡されます。

```rust:src/bin/cargo.rs(抜粋)
const USAGE: &'static str = "
Rust's package manager

Usage:
    cargo <command> [<args>...]
    cargo [options]

Options:
    -h, --help          Display this message
    -V, --version       Print version info and exit
    --list              List installed commands
    --explain CODE      Run `rustc --explain CODE`
    -v, --verbose ...   Use verbose output (-vv very verbose/build.rs output)
    -q, --quiet         No output printed to stdout
    --color WHEN        Coloring: auto, always, never
    --frozen            Require Cargo.lock and cache are up to date
    --locked            Require Cargo.lock is up to date

Some common cargo commands are (see all commands with --list):
    build       Compile the current project
    clean       Remove the target directory
    doc         Build this project's and its dependencies' documentation
    new         Create a new cargo project
    init        Create a new cargo project in an existing directory
    run         Build and execute src/main.rs
    test        Run the tests
    bench       Run the benchmarks
    update      Update dependencies listed in Cargo.lock
    search      Search registry for crates
    publish     Package and upload this project to the registry
    install     Install a Rust binary

See 'cargo help <command>' for more information on a specific command.
";
```

`Docopt::new()`関数がこの`Options`のところを解析し、コマンドライン引数を処理する`docopt::Parser`型の構文解析器を生成しています。その使い方は、上のコードを見ればほぼ明らかですね。詳しくは[ドキュメント](https://docs.rs/docopt/0.8.1/docopt/)を見るのが良いでしょう。

ちょっと脱線しますが、Rustでコマンドライン引数を処理するためのcrateは*docopt*だけではありません。[rustupコマンド](https://github.com/rust-lang-nursery/rustup.rs)が採用しているのは[*clap*というcrate](https://clap.rs/)です。これはかなり高機能で、cargoが力業でやっているようなサブコマンドの処理（これは次回以降に見ます）ですらも素直に処理できます。しかしながら、その分「usageを書けば終わり」というわけには行かず、[一つ一つ書いてやる必要があります](https://github.com/rust-lang-nursery/rustup.rs/blob/1.5.0/src/rustup-cli/rustup_mode.rs#L116)[^clap]。

[^clap]: *clap*の場合には、逆に、この記述から自動的にusageが生成されることが分かります。

このあたりはアプリケーションの複雑さや作者の好みによって、どちらを使うのかを選ぶことになるのでしょう。


# おわりに

というわけで、次回は今回後回しにした`Config`型などを見ていくことにします。その後で（次々回かな）、`execute_main_without_stdin()`関数の第一引数として渡されている`execute()`関数の中を見ていくことにしようと思います。


# 脚注