Rust で「パターン」について調べるとき match
式に注目しがちかもしれませんが、関数等の引数や let
宣言等、変数束縛 (代入のようなもの) をするような場所でも使えます。
他プログラミング言語における「分割代入」にあたる変数束縛もできます。
本記事では Rust の「パターン」の中でも「非構造化」についてまとめます。
※本記事では "destructuring" を「非構造化」と訳していますが、Rust の日本語版ドキュメントでは「分解」や「分配」と訳されています。
1. 「パターン」の概要
「パターン」は以下の箇所で利用できます。
-
let
宣言 - 関数またはクロージャの引数
-
match
式 -
if let
式 -
while let
式 -
for
式
「パターン」の役割は以下の 2 つです。
- 「値の構造が目的の構造と一致するか」を調べたいとき、その「目的の構造」を表現する
- 「値の構造が目的の構造と一致した」とき、その「構造」に基づいて値を非構造化し、変数を非構造化した値に束縛する (オプションな役割)
参考「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」