Rust で「パターン」について調べるとき match 式に注目しがちかもしれませんが、関数等の引数や let 宣言等、変数束縛 (代入のようなもの) をするような場所でも使えます。
他プログラミング言語における「分割代入」にあたる変数束縛もできます。

本記事では Rust の「パターン」の中でも「非構造化」についてまとめます。

※本記事では “destructuring” を「非構造化」と訳していますが、Rust の日本語版ドキュメントでは「分解」や「分配」と訳されています。

1. 「パターン」の概要

「パターン」は以下の箇所で利用できます。

let 宣言
関数またはクロージャの引数

match 式

if let 式

while let 式

for 式

「パターン」の役割は以下の 2 つです。

    1. 「値の構造が目的の構造と一致するか」を調べたいとき、その「目的の構造」を表現する

 

    「値の構造が目的の構造と一致した」とき、その「構造」に基づいて値を非構造化し、変数を非構造化した値に束縛する (オプションな役割)

参考「Patterns – The Rust Reference」
参考「Pattern Syntax – The Rust Programming Language」

2. 非構造化変数束縛

変数を束縛することから最終的に「識別子パターン」として解析されることになりますが、そこまで解析される過程等をまとめます。

(「ワイルドカードパターン」や「残余パターン」で変数束縛されないこともあります。)

参考「Identifier patterns – Patterns – The Rust Reference」

2.1. 「参照パターン」による参照外し

「参照パターン」によって参照外しをすることができます。

let foo = 23;

let bar = &foo; // 借用 (変数 bar は参照型)

let baz = *bar; // 参照外し演算子 * による参照外し

let &qux = bar; // 参照パターンによる参照外し
let something = |&foo| { // 参照パターンによる参照外し
    let bar = String::from(foo);
    println!("{}", bar);
};

let baz = "Baz";

let qux = &baz;

something(qux);

参考「Reference patterns – Patterns – The Rust Reference」

2.2. 「スライスパターン」による分割代入

let something = [10, 20, 30]; // Vec も可

let [foo, bar, baz] = something; // 配列の分割
println!("{} {} {}", foo, bar, baz);

let [foo, bar, _] = something; // 配列の分割: ワイルドカードパターン
println!("{} {}", foo, bar);

let [foo, ..] = something; // 配列の分割: 残余パターン
println!("{}", foo);

let [foo, bar @ ..] = something; // 配列の分割: 識別子パターン: @ サブパターン: 残余パターン
println!("{} {:?}", foo, bar);

let [foo, bar, ref baz] = something; // 配列の分割: 識別子パターン: 借用
println!("{} {} {}", foo, bar, *baz);

※非構造化では、分解した変数ごとに右辺側に借用演算子 (参照演算子) & を付けて借用することができないため、借用したい場合は左辺側の識別子 (変数名) 前にキーワード ref を付けて借用します。
※キーワード mut もキーワード ref と同様に左辺側の識別子 (変数名) 前で利用できます。

参考「Slice patterns – Patterns – The Rust Reference」

2.3. 「タプルパターン」によるタプルの分解

let something = (23, 'B', true);

let (foo, bar, baz) = something; // タプルの分解
println!("{} {} {}", foo, bar, baz);

let (foo, bar, _) = something; // タプルの分解: ワイルドカードパターン
println!("{} {}", foo, bar);

let (foo, ..) = something; // タプルの分解: 残余パターン
println!("{}", foo);

// スライスパターン以外では不可
// let (foo, bar @ ..) = something; // タプルの分解: 識別子パターン: @ サブパターン: 残余パターン

let (foo, bar, ref baz) = something; // タプルの分解: 識別子パターン: 借用
println!("{} {} {}", foo, bar, *baz);

※キーワード ref や mut は「スライスパターン」と同様。

参考「Tuple patterns – Patterns – The Rust Reference」

2.4. 「構造体パターン」による構造体の分解

struct Something {
    foo: i32,
    bar: char,
    baz: bool,
}

//
let something = Something {
    foo: 23,
    bar: 'B',
    baz: true,
};

let Something { foo, bar, baz } = something; // 構造体の分解
println!("{} {} {}", foo, bar, baz);

let Something { foo, bar, baz: qux } = something; // 構造体の分解: 識別子パターン (別名): qux = baz
println!("{} {} {}", foo, bar, qux);

let Something { foo, bar, baz: _ } = something; // 構造体の分解: ワイルドカードパターン: _ = baz
println!("{} {}", foo, bar);

let Something { foo, .. } = something; // 構造体の分解: 残余パターン
println!("{}", foo);

let Something { foo, bar, ref baz } = something; // 構造体の分解: 借用: ref baz = baz
println!("{} {} {}", foo, bar, *baz);

※キーワード ref や mut は「スライスパターン」と同様。
※厳密には、構造体の場合、キーワード ref や mut は「構造体パターンフィールド」として解析される場合と「識別子パターン」として解析される場合があります。

参考「Struct patterns – Patterns – The Rust Reference」

2.5. 「タプル構造体パターン」によるタプル構造体の分解

「タプル構造体パターン」では「構造体パターン」と同様に構造体のパスの一致が確認され、「タプルパターン」と同様に要素が分解されます。

struct Something(i32, char, bool);

//
let something = Something(23, 'B', true);

let Something(foo, bar, baz) = something; // タプル構造体の分解
println!("{} {} {}", foo, bar, baz);

let Something(foo, bar, _) = something; // タプル構造体の分解: ワイルドカードパターン
println!("{} {}", foo, bar);

let Something(foo, ..) = something; // タプル構造体の分解: 残余パターン
println!("{}", foo);

// スライスパターン以外では不可
// let Something (foo, bar @ ..) = something; // タプル構造体の分解: 識別子パターン: @ サブパターン: 残余パターン

let Something(foo, bar, ref baz) = something; // タプル構造体の分解: 識別子パターン: 借用
println!("{} {} {}", foo, bar, *baz);

※キーワード ref や mut は「スライスパターン」と同様。

参考「Tuple struct patterns – Patterns – The Rust Reference」

2.6. 列挙型の列挙子の分解

列挙型は列挙子の構造ごとに「パスパターン」や「タプル構造体パターン」、「構造体パターン」として解析され、分解されます。

(※「パスパターン」は変数を束縛しません。)

列挙型 Option や Result で使うことが多いかと思います。

let foo = Some(23);

if let Some(bar) = foo { // 列挙子の分解: タプル構造体パターン
    println!("{}", bar);
}
let foo: Result<(), _> = Err("Error");

if let Err(bar) = foo { // 列挙子の分解: タプル構造体パターン
    eprintln!("{}", bar);
    // process::exit(1);
}

参考「Destructuring – Patterns – The Rust Reference」
参考「Tuple struct patterns – Patterns – The Rust Reference」
参考「Path patterns – Patterns – The Rust Reference」

广告
将在 10 秒后关闭
bannerAds