11
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

[Rust] 「パターン」を用いた非構造化変数束縛

Last updated at Posted at 2021-08-26

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

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

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

1. 「パターン」の概要

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

  • let 宣言
  • 関数またはクロージャの引数
  • match
  • if let
  • while let
  • for

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

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

参考「Patterns - The Rust Reference
参考「Pattern Syntax - The Rust Programming Language

2. 非構造化変数束縛

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

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

参考「Identifier patterns - Patterns - The Rust Reference

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

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

let 宣言での例
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);

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

参考「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);

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

参考「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);

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

参考「Tuple struct patterns - Patterns - The Rust Reference

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

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

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

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

Option 型での例
let foo = Some(23);

if let Some(bar) = foo { // 列挙子の分解: タプル構造体パターン
    println!("{}", bar);
}
Result 型での例
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

11
16
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
11
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?