Rust、こんなときはこう書こう

こんにちは、[Rust2 Advent Calendar 2020](https://qiita.com/advent-calendar/2020/rust2)の1日目を担当させていただく[yasuo-ozu](https://qiita.com/yasuo-ozu)です。

今回は~~特にまとまったネタを思いつかなかったので~~tips集としてお送りします。有名なものばかりで恐縮ですが、何かありましたらご指摘いただければ幸いです。

# `Enum`型の要素を賢く使う

イテレータを Option でマップしたいとき、通常では以下のようにすると思います。

```rust
let v = vec![1usize, 2, 3];
let v = v.into_iter().map(|v| Some(v)).collect::<Vec<_>>();
```

実は、これは以下のように省略することが可能です。

```rust
let v = vec![1usize, 2, 3];
let v = v.into_iter().map(Some).collect::<Vec<_>>();
```

ここで、`Some(v)`は`Option<usize>`型の*Enum*値ですが、ただ`Some`とだけ書くと`fn(usize) -> Option<usize>` という関数になります。よって、`Iterator::map()`の中で直接用いることができます。

もちろん、`Option`だけでなく自分で定義した*Enum*でも使用できます。

```rust
enum MyError {
	IoError(std::io::Error),
	MyError(String),
	OtherError(Box<dyn std::error::Error>),
}
std::fs::read_to_string("myfile").map_err(MyError::IoError);
```

# 複数の条件を組み合わせる

プログラムから環境変数を使いたいと思った場合、以下のように書くと思います。

```rs
if let Ok(s) = std::env::var("MYENV") {
    println!("{:?}", s);
}
```

しかし、複数の環境変数を扱う場合に、すべてに対応する処理を書くのは少し面倒です。

```rs
if let Ok(s1) = std::env::var("MYENV1") {
    if let Ok(s2) = std::env::var("MYENV2") {
        println!("{:?} {:?}", s1, s2);
    }
}
```

これは、以下のようにかけます。

```rs
if let (Ok(s1), Ok(s2)) = (std::env::var("MYENV1"), std::env::var("MYENV2")) {
    println!("{:?} {:?}", s1, s2);
}
```

また、環境変数の候補が複数あり、どちらかセットされている方を選ぶという場合は以下のようにかけます。

```rs
match (std::env::var("MYENV1"), std::env::var("MYENV2")) {
    (Ok(s), Err(_)) | (_, Ok(s)) => println!("{:?}", s);
}
```

# `matches!` マクロを使おう

Rustでは`Eq`トレイトを実装していない型同士を比較することができません。実際、`Eq`を実装しない*Enum*を用いた以下の例は動きません。

```rs
enum TripleChoice {
    A,
    B,
    C
}
fn is_a(choice: TripleChoice) -> bool {
    choice == TripleChoice::A
}
```

ここで、`matches!`マクロを使って以下のようにすれば、`TripleChoice`が`Eq`を実装していなくても、`is_a`関数を定義できます。

```rs
fn is_a(choice: TripleChoice) -> bool {
    matches!(choice, TripleChoice::A)
}
```

## すばらしい数

別の変な例です。以下のようにすれば、「3以上12以下の偶数」を判定する関数を定義できます。

```rs
fn is_good_number(n: usize) -> bool {
    matches!(n, n @ 3..=12 if n % 2 == 0)
}
```

そうです、実は`matches!`マクロの中に書けるのは、[`match`式の条件に書けるものと同じ](https://doc.rust-lang.org/rust-by-example/flow_control/match.html)です。

[`matches!`マクロの定義](https://doc.rust-lang.org/stable/src/core/macros/mod.rs.html#244-251)も見てください。

# スライスの代入を使う

CLIのツールを作るとき、コマンドライン引数を使用することが多いと思いますが、間違った使い方を防ぐためにチェックをする必要があります。通常これは以下のように行うと思います。

```rs
let args = std::env::args().collect::<Vec<String>>();
if args.len() != 2 {
    eprintln!("bad args: {:?}", args);
}
let subcmd = args[1];
println!("{:?}", subcmd);
```

これは、スライスの代入を用いることでもっとスマートにかけます。

```rs
let args = std::env::args().collect::<Vec<String>>();
if let [_, subcmd] = args.as_slice() {
    println!("{:?}", subcmd);
} else {
    eprintln!("bad args: {:?}", args);
}
```

# 固定された要素数の`Iterator`生成

`Iterator`を使う局面で多いのは、扱う要素の数が可変である時です。しかしながら、場合によっては要素の固定された配列を`Iterator`として返したいときもあります。その時、以下のように書くことができます。

```rs
fn get_channels() -> impl Iterator<Item=usize> {
    vec![1, 3, 4, 6, 8, 10].into_iter()
}
```

もしくは、sliceを用いて以下のようにもかけます。

```rs
fn get_channels() -> impl Iterator<Item=usize> {
    [1, 3, 4, 6, 8, 10].iter().cloned()
}
```

しかしながら、場合によっては要素数0または1の`Iterator`を生成したい場合もあります。その場合は以下のように様々な方法があります。

```rs
Some(1).into_iter()
std::iter::once(1)
None.into_iter()
std::iter::empty()
```

# 重複を取り除く

## Dedup を使う方法

**[@kobae964](https://qiita.com/kobae964)さんのご指摘を踏まえて追加しました。**

```rs
let mut v = vec![1, 3, 4, 3, 7, 6, 4, 7];
v.sort();
v.dedup();
println!("{:?}", &v);
```


## HashSetを使う方法

```rs
let v = vec![1, 3, 7, 6, 3, 7];
let v = v.into_iter().collect::<HashSet<_>>().into_iter().collect::<Vec<_>>();
```

ただし、取り出すときに要素の順番が不定になります(順序が保存されない)。数値の大小の順番で取り出したいときは代わりに`BTreeSet`を使います。

```rs
let v = v.into_iter().collect::<BTreeSet<_>>().into_iter().collect::<Vec<_>>();
```

## Dedupを自分で実装する場合

```rs
let mut v = vec![1, 3, 4, 3, 7, 6, 4, 7];
v.sort();
let v = v
    .into_iter()
    .scan(None as Option<usize>, |stat, n| {
        if let Some(stat) = stat {
            if stat == &n {
                return Some(None);
            }
        }
        *stat = Some(n);
        Some(*stat)
    })
    .flatten()
    .collect::<Vec<usize>>();
println!("{:?}", &v);
```

# feature flags とモジュール

Rustでは、コンパイル時にプログラムの一部を有効化もしくは無効化するときにfeature flagsというものを使用できます。例えば、`Cargo.toml`に

```Cargo.toml
[features]
my-feature = []
```

と書いた上で、

```rs
#[cfg(feature = "my-feature")]
pub fn my_func() { /* ... */ }
```

とやれば、`my_func()`は`my-feature`が有効な時のみコンパイルされることになります。

しかし、このように制御したい定義が1個だけではなく、多数に上る場合、モジュールを切り分けた方が楽です。

```rs
fn outer_func() { /* ... */ }
#[cfg(feature = "my-feature")]
pub mod my_feature_mod {
    pub fn my_func1() {
        super::outer_func()
    }
}
```

ただし、この場合外部から`my_func1()`を呼び出す場合、`my_feature_mod::my_func1()`のようにする必要がありますし、モジュール内から外側の定義を用いる場合も`super::`をつけて参照する必要があります。
これを省略するには、以下のようにします。

```rs
#[cfg(feature = "my-feature")]
pub use my_feature_mod::*;

#[cfg(feature = "my-feature")]
mod my_feature_mod {
    use super::*
    pub fn my_func1() {
        outer_func()
    }
}
```

# HashSetじゃなくてBTreeSet

Rust言語で数値の集合を取り扱いたい場合が時々あります。たとえば、選んだ数値の組み合わせによって点数が決まるゲームを考えます。ただし、同じ数値は1回しか選べず、選び方は順不同だとします。

このとき、次のような関数を定義することを考えます。

```rs
fn get_score(set: HashSet<usize>) -> usize {
    /* ... */
}
```

この関数内で、`set`の内容に応じた分岐を行ってもよいのですが、折角なので集合とそれに対応する得点を別に宣言することにしましょう。

```rs
let mut map: HashMap<HashSet<usize>, usize> = HashMap::new();
map.insert(vec![1, 3].into_iter().collect(), 10);
map.insert(vec![1, 3, 7].into_iter().collect(), 20);

fn get_score(set: HashSet<usize>, map: &HashMap<HashSet<usize>, usize>) -> usize {
    map.get(&set).cloned().unwrap_or(0)
}
```

しかし、これでは動作しません。なぜなら、`HashSet`自体が`trait Hash`を実装していないため、`HashMap`のKeyとして用いることができないからです。

これは、`HashSet`の代わりに`BTreeSet`を使えば解決します。

```rs
let mut map: HashMap<BTreeSet<usize>, usize> = HashMap::new();
map.insert(vec![1, 3].into_iter().collect(), 10);
map.insert(vec![1, 3, 7].into_iter().collect(), 20);

fn get_score(set: BTreeSet<usize>, map: &HashMap<BTreeSet<usize>, usize>) -> usize {
    map.get(&set).cloned().unwrap_or(0)
}
```

ただ、`BTreeSet`を使うためには要素が`trait Ord`を実装していなければならないので、注意してください。
詳しくは[この記事](https://qiita.com/yasuo-ozu/items/f4eea40e2045d0d700e9)を参照

# 結合型として`Box` を使う

例えば、Rustでイテレータを返す関数を実装する場合、以下のようにします。

```rs
fn iter_number() -> impl Iterator<Item=usize> {
    0usize..
}

for n in iter_number().take(10) {
    println!("{}", n);
}
```

ここで、`0usize..`は0から始まって永遠に加算されていくイテレータを生成します。また、`impl Iterator`はイテレータの型を省略する記法で、ここではコンパイル時に`RangeFrom`という型に変換されます。

では、この`iter_number()`に平方数を出力する機能を実装してみましょう。

```rs
fn iter_number(squared: bool) -> impl Iterator<Item=usize> {
    let it = 0usize..;
    if squared {
	it.map(|n| n * n)
    } else {
	it
    }
}

for n in iter_number(false).take(10) {
    println!("{}", n);
}
```

これは、一見良さそうに見えますが、実は動きません。これは、Rustの「関数は一つの型しか返せない」という制約によるものです。
ここで`it`は先程説明したように`RangeFrom`という型の変数になっていますが、これを`map()`によって変換すると、`Map`という型に変換されます。この関数は、`RangeFrom`と`Map`の両方を返す可能性があり、`impl Iterator`に対応する具体的な型が決まらないためエラーとなるのです。

ここで、Rustの手動結合型機能である`Box`を用います。これによって、`RangeFrom<_>`と`Map<_>`はどちらも`Box<dyn Iterator<Item=_>>`という型にキャストできます。また、`Box<T> where T: Iterator<Item=_>`も`Iterator<Item=_>`を実装しているため、`impl Iterator`としてreturn することができます。

```rs
fn iter_number(squared: bool) -> impl Iterator<Item=usize> {
    let it = 0usize..;
    if squared {
        Box::new(it.map(|n| n * n)) as Box<dyn Iterator<Item=_>>
    } else {
        Box::new(it) as Box<dyn Iterator<Item=_>>
    }
}

for n in iter_number(true).take(10) {
    println!("{}", n);
}
```

## traitを用いる例

同じことはtraitを使う場合にも言えます。というのも、trait内で関数を宣言する場合、`impl`は使用できないからです。この場合もやはり`impl Iterator`の代わりに、以下のように`Box<dyn Iterator<Item=usize>>`を用いることができます。

```rs
trait IterMaker {
    fn iter(self) -> Box<dyn Iterator<Item = usize>>;
}
struct NumberIterMaker();
impl IterMaker for NumberIterMaker {
    fn iter(self) -> Box<dyn Iterator<Item = usize>> {
	Box::new(0usize..) as Box<dyn Iterator<Item = _>>
    }
}

fn print_iter(it: impl IterMaker) {
    for v in it.iter().take(10) {
	println!("{}", v);
    }
}

let im = NumberIterMaker();
print_iter(im);
```