2020/09/08

Rustのif letが便利

if let

Rustではmatch式の省略形とも言えるif letというものがあります。

実は私、最近までこれを使わずmatch式をメインに使っていました。例えば以前の記事でRustのsubsliceを使ってL99問題をやってみるというものを書きましたが、そのコードのすべてがmatch式によるものでした。

しかし、今回if letを使って書き直してみると、何ともすっきりとしたコードになり、すっかりif letが気に入ってしまいましした。

例えばL-01は以下のようにすっきりとした形になります。


fn lst(list: &[T]) -> Option<&T> {
    if let [.., elt] = list { Some(elt) } else { None }
}


-------
実行結果
-------

let v = vec![1,2,3,4,5];
assert_eq!(lst(&v), Some(&5));

let empty_v: Vec<String> = vec![];
assert_eq!(lst(&empty_v), None);

let one_elt_v = vec!['A'];
assert_eq!(lst(&one_elt_v), Some(&'A'));

L-02では


fn lst_two(list: &[T]) -> Option<(&T, &T)> {
    if let [.., elt1, elt2] = list { Some((elt1, elt2)) } else { None }
}


-------
実行結果
-------

let v = vec![1,2,3,4,5];
assert_eq!(lst_two(&v), Some((&4, &5));

let empty_v: Vec<String> = vec![];
assert_eq!(lst_two(&empty_v), None);

let one_elt_v = vec!['A'];
assert_eq!(lst_two(&one_elt_v), None);

という感じになりました。

例えばOptionやResultなどのようにパターンが2択の時にはシャシャっと書けて大変便利です。さらにsubsliceがより使いやすくしてくれているように思います。

ちなみにL-03およびL-04は以下のようになりました。


//L-03
fn at<T>(list: &[T], k: usize) -> Option<&T> {
    if let [hd, tl @ ..] = list {
        if k == 1 {
            Some(hd)
        } else {
            at(tl, k - 1)
        }
    } else { None }
}

// L-04
fn length<T>(list: &[T]) -> usize {
    fn aux<T>(n: usize, l: &[T]) -> usize {
        if let [_, tl @ ..] = l { aux(n + 1, tl) } else { n }
    }
    aux(0, list)
}

無理に使う必要はないものの(基本match式で書ける)、知っておくと便利なif let