3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Let chains実装記念】パターンマッチテクニック集【Rust】

Last updated at Posted at 2025-07-15

先月、 Rust 1.88 がリリースされました :cracker: :cracker:

特に「 if let 式で let&& で繋げられる」 Let chains というのが目玉機能として挙げられています1

Let chains
fn jsons_json_in_json_article_checker(contents: &str) -> anyhow::Result<bool> {
    // if let 式
    if let Contents {
        author,
        content: Content::Other { body: json_str },
        ..
    } = serde_json::from_str(contents)?
    // 1.88からは、さらに条件をくっつけられる!
    && author == "Json"
    // 1.88からは、let もくっつけられる!
    && let JsonInJsonContent::Article { body } = serde_json::from_str(&json_str)? {
        println!("It's Json's Article: {}", body);
        Ok(true)
    } else {
        Ok(false)
    }
}
全体
Rust
#![allow(unused)]

use serde::Deserialize;

#[derive(Deserialize)]
struct Contents {
    id: usize,
    author: String,
    content: Content,
}

#[derive(Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
enum Content {
    Article {
        title: String,
        body: String,
    },
    Other {
        body: String,
    },
}

#[derive(Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
enum JsonInJsonContent {
    Article {
        body: String,
    },
    Other {
        body: String,
    },
}

fn jsons_json_in_json_article_checker(contents: &str) -> anyhow::Result<bool> {
    if let Contents {
        author,
        content: Content::Other { body: json_str },
        ..
    } = serde_json::from_str(contents)?
    && author == "Json"
    && let JsonInJsonContent::Article { body } = serde_json::from_str(&json_str)? {
        println!("It's Json's Article: {}", body);
        Ok(true)
    } else {
        Ok(false)
    }
}

fn main() -> anyhow::Result<()> {
    let contents = r#"{
    "id": 13,
    "author": "Json",
    "content": {
        "type": "other",
        "body": "{ \"type\": \"article\", \"body\": \"Today is Friday.\" }"
    }
}"#;

    let true = jsons_json_in_json_article_checker(contents)? else {
        println!("Pattern matching failed...");
        return Ok(());
    };
    
    Ok(())
}

Playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=9ff3d4246ae2aeea4ac956ebd3409734

とても嬉しい機能が追加されたので、 let-else文のノリで 記事を書こうかと最初は思ったのですが、素晴らしい 先達記事 がありましたので本記事では if let 式の周辺知識、という位置付けでパターンマッチのテクニック集を紹介したいと思います!

基礎事項についてはほぼ公式ドキュメントのまとめ直しです。

一次ソースに当たりたい方は以下も見てみてください!

TODO: TL;DR的なものの記載

Rustのパターンマッチ

if let 式の let 部分ではRustのパターンマッチ機能が利用できます。

本節ではそもそもパターンマッチってなんだっけ...?というところからおさらいしたいと思います。


基本としては「 match 式の左側に列挙するもの」という認識で大体あっています。

他の言語でもあるように列挙体で分岐する場合もあれば...

列挙体での分岐
#[derive(Clone, Copy)]
enum Fruits {
    Apple,
    Banana,
    Orange,
}

impl Fruits {
    fn get_price(self) -> u32 {
        match self {
            Fruits::Apple => 200,
            Fruits::Banana => 100,
            Fruits::Orange => 250,
        }
    }
}

プリミティブ型2の値で分岐する場合もあります。

プリミティブ型での分岐
let age = 10;

let s = match age {
    0 => "生まれたばかり",
    1 | 2 => "1, 2歳",
    3..=6 => "幼稚園",
    7..=15 => "義務教育",
    16..=18 => "未成年",
    _ => "成人",
};

println!("あなたの現在: {s}"); // 義務教育

let chr = '@';

match chr {
    'a' | 'A' => println!("エー"),
    c @ 'A'..'z' => println!("{c} はアルファベット"),
    c if c.is_ascii() => println!("{c} はアスキー範囲の文字"),
    c => println!("その他: {c}"),
}

もっと面白い例もあるのですがネタが尽きてしまうので記事の後半で...

この先 match 式に限らない色々な構文を紹介していく予定ですが、その前にパターンマッチおよび "パターン" について解説します!

パターンマッチの役割: 「分解」「束縛」「合致判定」

パターンマッチには大体3つの役割があります。

まず2つは「分解」と「束縛」です。TSなどにもある「分割代入」と同じ機能ですね。

分割代入の例
fn get_current_location() -> (f64, f64) { /* 省略 */ }

let (x, y) = get_current_location();

この let 文では、 get_current_location の返り値を (x, y) というタプルに分解しています。ついでに、 x , y という変数に束縛され、以降同スコープ内で変数 xy が使えるようになっています。

分解はタプルの他にも構造体・列挙体・配列など色々な構文要素で可能です!

Rust
#[derive(Clone, Copy, Debug)]
struct Point {
    x: f64,
    y: f64,
}

fn main() {
    // 通常の変数宣言
    let p = Point { x: 0., y: 10. };

    let Point { x: a, y: b } = p; // 分解して変数 a, b に束縛
    println!("{a} {b}");

    // 変数名がフィールド名と同じで良い場合
    let Point { x, y } = p;

    println!("{x} {y}");
}

Playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=33ed488bc9638646b00844241d3f78bc

隙有場合合致すきあらばパターンマッチ

ここで逆転の発想として押さえておいてもらいたいのは、Rustで 「変数の束縛(あるいは変数宣言)ができるならその箇所はパターンマッチである」 という事実です。

以下は後述の論駁不可能パターンしか取れませんが全て「パターンマッチチャンス :sparkles::sparkles: 」なのです!

パターンマッチチャンス特集
#[derive(Clone, Copy, Debug)]
struct Point {
    x: f64,
    y: f64,
}

impl Point {
    fn dist(
        self,
        // 関数の引数でパターンマッチ!
        Point { x: x1, y: y1 }: Point,
    ) -> f64 {
        // もちろんletでパターンマッチ!
        let Point { x: x0, y: y0 } = self;

        ((x0 - x1).powi(2) + (y0 - y1).powi(2)).sqrt()
    }
}

fn main() {
    // ただの束縛 (でもパターンマッチの一つと見れる!)
    let points = [
        Point { x: 0., y: 0. },
        Point { x: 1., y: 2. },
        Point { x: 2., y: 3. },
        Point { x: 5., y: 0. },
        Point { x: 0., y: 0. },
    ];

    let mut total_dist = 0.;
    let mut pre_p = points[0];
    // for でもパターンマッチ!
    for &p @ Point { x, y } in &points[1..] {
        println!("now: ({x}, {y})");

        total_dist += p.dist(pre_p);

        // let がない時は流石に束縛(もとい再代入)しかできない
        pre_p = p;
    }
    
    let total_dist_1 = points[1..]
        .iter()
        .fold(
            (0., points[0]),
            // クロージャ引数でもパターンマッチ!
            |(total, pre_p), &p| (p.dist(pre_p) + total, p)
        ).0;

    println!("{total_dist}, {total_dist_1}");
    assert_eq!(total_dist, total_dist_1);
}

Playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=16ba58a7ef779e85261e37971ffd2236

恣意的な例で少し冗長ですが、上記ソースコードでは以下のパターンマッチが登場しています

  • 関数引数でのパターンマッチ: Point { x: x1, y: y1 }: Point
  • let 文でのパターンマッチ: let Point { x: x0, y: y0 } = self;
  • for 文でのパターンマッチ: for &p @ Point { x, y } in &points[1..] {...}
  • クロージャ引数でのパターンマッチ: |(total, pre_p), &p| {...}

本記事後半ではパターンマッチが使える事例を個別に一応紹介していこうと考えていますが、 そもそも変数束縛ができる箇所は大体パターンマッチだ という風に捉えておいた方が自然に見えるんじゃないかと思います。

ただし、以下に示す様な例外もあります

  • let がないミュータブル変数への再代入
  • static / const による定数宣言

合致判定

最後の役割が名前通りパターンにマッチするかどうかの検証です。ただし、この役割は後述の「論駁可能パターン」のみが持ちます。

Rust
let mut v: Vec<usize> = (1..10).collect();

while let Some(n) = v.pop() { // パターンにマッチしている間のみ実行
    match n {
        m if m % 2 == 0 => println!("{m}は偶数"),
        m => println!("{m}は奇数"),
    }
}

ここで示した while let 文や match 式は論駁可能パターンによるパターンマッチで、論駁可能パターンの特権である「パターン合致検証」を行ない、合致する時のみスコープ内の文を実行しています。これらのパターンマッチも、前節で紹介した「分解」と「束縛」の役割も担っている点にも注目です。

まとめ: パターンマッチの役割

  • 分解
  • 束縛
  • 合致判定

論駁可能パターンと論駁不可能パターン

参考: 論駁可能性: パターンが合致しないかどうか

ここまでの説明ですでに何度か顔を出していましたが、パターンマッチのパターンは、論駁(可能|不可能)パターンの2つに分類されます!

  1. 論駁可能パターン ( refutable pattern )
  2. 論駁不可能パターン ( irrefutable pattern )

なぜ「論駁」?

論駁というのは「反論」と大体似た意味の言葉です。 refutable という英単語が割り当てられているのですが、それの直訳が「論駁」なので和訳ドキュメントは論駁なのだろうと思います。

...かっこいいからヨシ!

論駁可能パターン

受け入れ・束縛に失敗することがある パターンマッチのパターンを論駁可能なパターンといいます。 match 式のアームや if let 式に用いられるパターンは(基本的に3)論駁可能パターンです。

論駁不可能パターンたち
#[derive(Clone, Copy, Debug)]
enum Fruits {
    Apple,
    Banana,
    Orange,
}

struct PurchaseResult {
    fruits: Option<Fruits>,
    change: u32,
}

impl Fruits {
    fn get_price(self) -> u32 {
        match self {
            // Appleじゃないこともある
            // よって論駁可能パターン
            Fruits::Apple => 200,
            
            // Bananaじゃないことも当然ある
            // 論駁可能パターン
            Fruits::Banana => 100,

            // Orangeに当てはまるとも限らない
            // 論駁可(ry
            Fruits::Orange => 250,
        } // ただしどれかでは絶対ある
    }

    fn try_purchase(self, payment: u32) -> PurchaseResult {
        let price = self.get_price();
        if payment >= price {
            PurchaseResult {
                fruits: Some(self),
                change: payment - price,
            }
        } else {
            PurchaseResult {
                fruits: None,
                change: payment
            }
        }
    }
}

fn main() {
    let res = Fruits::Banana.try_purchase(300);

    // 買えない時もあればお釣りが0円の時もあり、以下のパターンマッチは失敗する可能性がある
    // 論駁可能パターン
    if let PurchaseResult { fruits: Some(f), change: ch @ 1.. } = res {
        println!("{f:?} が購入できてお釣りは {ch} 円だった!");
    }
}

Playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=9d2c7b8d3325297f7a6002f68de35acd

Fruits::ApplePurchaseResult { fruits: Some(f), change: ch @ 1.. } は常に受け入れられる・束縛できるとは限らないので、全て論駁可能パターンというわけです。

PurchaseResult {...} の例を見るとわかる通り、論駁可能かどうかはパターンの複雑さには特に関係なく、純粋に 「常に受け入れ・束縛可能かどうか」 のみで決まります。

補足: 「束縛」ではなく「受け入れ・束縛」と書いたのは、 Fruits::Apple のように変数が一切生まれないパターンもあるためです。広義的にこれを束縛と捉えても良いかも...?

別な例
#[derive(Clone, Copy)]
struct Point {
    x: u32,
    y: u32,
}

fn main() {
    let point = Point { x: 10, y: 20 };

    // こちらは後述の論駁不可能パターン
    let Point { x, y } = point;

    println!("x = {}, y = {}", x, y);

    // xが常に0とは限らないので、論駁可能パターン
    // let Point { x: 0, y } = point;
    /* refutable pattern in local binding: `Point { x: 1_u32..=u32::MAX, .. }` not covered
       `let` bindings require an "irrefutable pattern", ...
       と怒られる
    */
    // if letやmatchなら論駁可能パターンを取れる
    if let Point { x: 0, y } = point {
        println!("x = 0, y = {}", y);
    }
}

論駁不可能パターン

どんな場面でも絶対に受け入れ・束縛に成功する パターンマッチを論駁可能なパターンと言います。

通常の変数束縛はある意味で絶対成功するパターンマッチです。その他にも、構造体の分解なども必ず成功するため論駁不可能パターンとして利用可能です。

通常の let 文が取れるパターンは論駁不可能パターンである必要があります。逆に言えば 論駁不可能なら意外になんでも書くことができます。以下例です。

次のlet文は全部コンパイルOK
// 普通の変数宣言
let p = Point { x: 0., y: 0. };

// 分解構文用途
let Point { x, y } = p;

// 配列もいけます
let [a, b, c, ..] = [0, 1, 2, 3, 4];

// 以下は処理としてはnop
let 0..=255_u8 = 10; // u8 の変数は必ず0から255
let () = {}; // ユニット値は当然ユニット値に束縛できる

Playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=d484f2a7776da0f5ea80399716526d8c

パターンマッチ全体のテクニック

ここまででパターンマッチで必ず押さえておかなければならない基礎事項もとい直感については説明できたと思います。ここからは、いよいよパターンマッチのテクニック集を見せていきたいと思います!

:sparkles: テクニック1 :sparkles: _.. で値を無視する

分解構文的にパターンマッチを見た際、利用しないフィールドがある時もあるでしょう。 _.. はそんな時に利用できたりします!

Rust
#[derive(Clone, Copy, Debug)]
struct Point {
    x: f64,
    y: f64,
}

impl Point {
    fn dist_of_x(
        self,
        Point { x: x1, y: _ }: Point,
    ) -> f64 {
        let Point { x: x0, y: _ } = self;

        (x0 - x1).abs()
    }
}

Playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=efadb21cae114107df99102817c44274

上記の例では、 y の値は使用しないため _ としています。

.. を使えば、「以降のフィールドを無視」といったことも可能です。たくさんフィールドがある際などに便利です。

Rust
struct Settings {
    interval: Duration,
    repeat_num: usize,
    hensuumei: String,
    kanngaeruno: bool,
    mendoukusakunatta: char,
}

let Settings {
    interval,
    repeat_num,
    ..
} = s;

Playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=3e05d94680e2b47f0213c5d0724dc950

__x の違い

変数の接頭辞に _ をつけることで未使用変数として扱えます。

実はこれは let _ = ..._ とは挙動が少し違います。

  • _x実際に束縛は行われる、すなわち所有権が奪われたりする
  • _: 束縛すら行われない。所有権も奪われない

_ は特別な記法として覚えておいて損はないでしょう。

:sparkles: テクニック2 :sparkles: | でパターン連結し論駁不可能にする

| を使うことで複数パターンを結合することができます。match 式のアームでよく使える記法です!

Rust
match c {
    'a' | 'A' => println!("a か A"), // 'a' と 'A' のパターンを結合
    c => println!("{c}"),
}

ところで、 let 文は論駁不可能なパターンしか用いることができないのでした。しかし、 論駁不可能なら文句なし なわけです。というわけで、実は次のソースコードは問題ありません!

最初か後ろをバインド
fn hoge(opt: Option<usize>, default: usize) {
    let ((Some(n), _) | (_, n)) = (opt, default);

    println!("{n}");
}

Playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=3d2279a2c4cd1b95936a0d0b8e0803b3

Some(n) みたいなものを利用して束縛を行う場合 if let 式や後述の let else 文が必要になりそうなものですが、次の2点を守っていれば論駁不可能なので例の様な let 文が書けます!

  • | でくっつけたパターンのどれかには該当するか
  • どのパターンに該当したとしても、同じ型の同じ変数(例では n: usize )が同じだけ用意されること

:sparkles: テクニック3 :sparkles: 整数型と char 型は .. / ..= で範囲指定できる

Rustでは 1..10 で1以上10未満、 1..=10 で1以上10以下、といった具合に範囲型を作ることができます。

実はパターンマッチにおいて、整数型と char 型(文字型のこと)のみこの記法を用いたパターンを書くことが可能です!

Rust
let chr = 'Z';

match chr {
    'A'..'z' => println!("アルファベット"),
    c => println!("その他: {c}"),
}

ちなみに全パターンを網羅(exhaustive)する必要がある match 式のアームに使った場合、この記法も使ってもしっかりと網羅性チェックは行われるので安心です :thumbsup:

使用頻度は高くないと思いますが、知っておいて損はないでしょう。

:sparkles: テクニック4 :sparkles: @ で値を束縛しつつパターン検証

「ある値があるパターンにマッチするかを調べたい」、でも、「元の値を利用したい」、そんな時に利用できるのが @ です!

Rust
let age = 15;

match age {
    ..13 => println!("子供"),
    n @ 13..=19 => println!("{n} 歳はティーンエイジャー"),
    n => println!("{n} 歳は大人"),
}

上記では範囲指定でのみ使っていますが、列挙型を深いネストと共にパターンマッチさせる場合などにも便利だったりします

Rust
enum Gender {
    Male,
    Female
}

struct Person {
    first_name: String,
    last_name: String,
    gender: Gender,
}

use Gender::*;

impl Person {
    fn greet(self) {
        match self {
            // パターンマッチで男性であることを確かめつつ、 p に全体を入れる
            p @ Person { gender: Male, .. } => println!("Hello, Mr. {}", p.name()),
            p @ Person { gender: Female, .. } => println!("Hello, Ms. {}", p.name()),
        }
    }
    
    fn name(&self) -> String {
        format!("{} {}", self.first_name, self.last_name)
    }
}

Playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=b93f423a818eb6b80219936f8d936f3c

:sparkles: テクニック5 :sparkles: ref, ref mut, &

ref, ref mut, & を利用することで、参照化/参照外しが可能になります!

  • ref / ref mut: 変数の束縛時に、 ref なら不変参照、 ref mut なら可変参照とする
  • &: 評価される値が参照の時、参照を外した値にする ( Copy トレイト付与型を基本に捉えておいた方がよい)

refref mut は分割代入時に変数を参照化するのに使うと便利です。

ref / ref mut
fn count(mut self) -> Self {
    let Counter {
        ref mut counter,
        ref diff, 
    } = self;
        
    *counter += *diff;
    
    self
}

Playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=066543291d39c949c6bfdb82cf176434

とはいえ「ref / ref mut でないと絶対に実現できない処理」というのに筆者は出会ったことがなく、普通にRustを書いている分には必要としない機能かもしれません。

& の方はクロージャの引数でしばしば目にすることがあります。

Rust
fn main() {
    let v: Vec<usize> = (1..=10).collect();

    // iter だと v: &usize なので &v と書いて参照外し
    let s: usize = v.iter().map(|&v| v * v).sum();
    
    println!("{} = {s}",
        v.iter()
            .map(|i| format!("{i}*{i}"))
            .collect::<Vec<_>>()
            .join(" + ")
    );
}

Playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=50226a652aa49f6c4a7ab0864dfb93cf

イメージとしては &10 みたいな値がある時、 &10 に分解して v の方に 10 を束縛している感じでしょうかね...?

論駁不可能パターンが使える構文集

記事冒頭で「分割代入のあるところにパターンマッチあり」と紹介しました。とはいえ、結局具体的にどのような構文が使えるかの言及はしていなかったので、記事の残りではパターンマッチを使える構文集を論駁可能・不可能に分けて紹介していきたいと思います。

数が少ない & 基本的なものであるという理由から論駁不可能パターンからです。

let

もう何度も登場したお馴染みの束縛構文です。もはや説明不要!

Rust
let Point { x, y } = p;

良い機会なので「ちなみに」として書きますが、 let には変数宣言だけを行う機能もあります。

Rust
fn func(flag: bool) -> usize {
    let (val, _): (usize, usize);

    // val には一度だけ代入可能
    if flag {
        val = 10;
    } else {
        val = 20;
    }

    val * val
}

Playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=01f7e2539251d24b1d8b664e100240fb

例で書いた様に、パターンを利用して変数宣言を書くと非常にシュールですね... :sweat:

関数・クロージャの引数

先に挙げた例の通り、関数やクロージャの引数部分でパターンマッチを行うことができます!

Rust
#[derive(Clone, Copy, Debug)]
struct Point {
    x: f64,
    y: f64,
}

impl Point {
    fn dist(
        self,
        // 関数の引数でパターンマッチ!
        Point { x: x1, y: y1 }: Point,
    ) -> f64 {
        // もちろんletでパターンマッチ!
        let Point { x: x0, y: y0 } = self;

        ((x0 - x1).powi(2) + (y0 - y1).powi(2)).sqrt()
    }
}

クロージャはともかく、関数引数部分での実用的な使い方例としては、Rustでオプショナル引数的なものを用意したくなった際、引数用構造体があると捗るのですが、その構造体を分解したい時に多少可読性が上がるかもという感じです。(未使用変数がわかるため)

Rust
struct FuncInput {
    arg1: usize,
    arg2: u32,
    arg3: u64,
    arg4: u128,
    arg5: i32,
    arg6: i64,
    arg7: i128,
}

impl Default for FuncInput {
    fn default() -> Self {
        Self {
            arg1: usize,
            // arg2 以降略
        }
    }
}

// ここで分解構文!
fn func(FuncInput {
    arg1,
    arg2,
    arg3,
    arg4,
    arg5,
    arg6,
    arg7,
}: FuncInput) {
    // 略
}

fn main() {
    // 引数が多い関数は引数用構造体を用意してDefaultをimplさせておくのが吉
    func(Default::default());
}

Playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=2b3260133a9b9d50e713d2da611dbd9a

クロージャの方はワンライナーにするためによく見かける気がします。

再掲
let total_dist_1 = points[1..]
    .iter()
    .fold(
        (0., points[0]),
        // クロージャ引数でパターンマッチ
        |(total, pre_p), &p| (p.dist(pre_p) + total, p)
    ).0;

for

for xxx in vvv {...}xxx 部分にパターンを書けます!

Rust
struct Point {
    x: usize,
    y: usize,
}

fn main() {
    let points = [
        Point { x: 0, y: 0 },
        Point { x: 1, y: 1 },
    ];
    
    for Point { x, y } in points {
        println!("x: {x} y: {y}");
    }
}

Playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=6a67f7765d3d0745ae79507226343e37

それだけなのですけども、 let 同様便利ですね。

特にイテレータに対して enumerate を呼んだ時などには、クロージャにしろ for 文にしろインデックスと値についてタプルでの受け取りが発生してしばしば書くんじゃないかと思います!

(以下編集中です :bow: )

論駁可能パターンが使える構文集

残りは、論駁可能パターンを使う構文、すなわち条件分岐的な要素が入ってくる構文集です!論駁可能パターンを受け取る構文、不可能よりも結構ありますね

match

Rust
fn fizz_buzz(n: usize) -> String {
    match (n % 3, n % 5) {
        (0, 0) => "fizzbuzz".to_string(),
        (0, _) => "fizz".to_string(),
        (_, 0) => "buzz".to_string(),
        _ => n.to_string()
    }
}

網羅性 (exhaustive)

if ガード

if let

Rust 1.88 の主役、 if let は冒頭のソースコードから顔を出してきました!

while let

let else

以前書いた記事の方が詳しいのでそちらもぜひご一読いただけると幸いです!

Rustのlet-else文気持ち良すぎだろ #Rust - Qiita

never

matches! マクロ

まとめ

  1. proc_macro::Span::line 等、マクロで使用する Span にあるメソッドの安定化が個人的には一番アツかったですが、それはまた別な話...いつか話せればと思います

  2. Rustの組み込み型のうち、 Copy トレイトが付与されている型(スタックに保存できる型)という認識で大体あっています。

  3. match 式のデフォルトパターンは論駁不可能パターンですし、論駁不可能パターンが一つだけある match 式も書けます。ただ後者に関しては let 文で良いかなと思います。

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?