FrozenLib 2018-12-17T16:57:54.443Z https://frozenlib.net/ FrozenLib Hexo Rustのパターンマッチを完全に理解した (2) https://frozenlib.net/blog/2018-12-18_rust-pattern-match-2/ 2018-12-17T15:00:00.000Z 2018-12-17T16:57:54.443Z <![CDATA[

本記事はRustその2 Advent Calendar 2018 の17日目の記事です。

本blogで Rustのパターンマッチを完全に理解した を書いてから約1年が経過しました。

その間にRustにはパターンマッチ関連の機能もいくつか追加され、以前の内容ではパターンマッチを完全に理解したとは言えなくなってしまいました。

そこで、本記事では

  • Rustに最近追加されたパターンマッチ関連の仕様
  • 最近追加されたわけではないが、以前の記事で書き忘れた機能

について紹介したいと思います。

フィールド名の省略

まずは前回書き忘れた事を。
(Version 1.17.0 2017-04-27) からできた事ですが・・・

次のように構造体をフィールド名と同名の変数で初期化する場合、

1
2
3
4
5
6
7
8
struct Foo {
xxx: u8,
yyy: u8,
}

fn create_foo(xxx: u8, yyy: u8) {
let _value = Foo { xxx: xxx, yyy: yyy };
}

下のようにフィールド名を省略することができる、という事は皆さんご存知かと思います。

1
2
3
fn create_foo(xxx: u8, yyy: u8) {
let _value = Foo { xxx, yyy };
}

この記法はパターンマッチでも使用できます。

つまり、次のようにフィールド名と同じ名前の変数に束縛する場合は・・・

1
2
3
fn bind_foo(foo: Foo) {
let Foo { xxx: xxx, yyy: yyy } = foo;
}

下のように、フィールド名を省略することができます。

1
2
3
fn bind_foo(foo: Foo) {
let Foo { xxx, yyy } = foo;
}

もちろん、参照を束縛する場合もOK。

1
2
3
fn bind_foo_ref(foo: Foo) {
let &Foo { ref xxx, ref yyy } = &foo;
}

&の省略と既定の束縛モード

Version 1.26.0 (2018-05-10) で「えっ?わかりにくくない?」と思うような機能が入りました。

まず、次の例を見てください。

1
let &(ref a, ref b) = &(1, 2);

「タプルの参照」から「タプルの要素の参照」を取得しています。
参照からは所有権を奪えないため、このパターンはよく使われますね。

このような&が含まれるパターンでは & が省略できるようになりました。
つまり、次のように書けるという事です。

1
let (ref a, ref b) = &(1, 2);

さらに & が省略されたパターンの内側では変数名の前に ref を付けなくても ref を付けた時と同様に参照が束縛されるようになりました。
つまり、ref も省略できるという事です。

1
let (a, b) = &(1, 2);

& はいくつでも省略できます。

1
let (a, b) = &&&&&&&&&(1, 2);

ref を付けなくても参照が束縛されるのは & が省略されたパターンの内側(下の例では a , b )だけです。
& が省略されたパターンの外側(下の例では x, y )では値が束縛されます。

1
2
3
let ((a, b), &(x, y)) = (&(1, 2), &(3, 4));
// a, b は &i32 (参照が束縛される)
// x, y は i32 (値が束縛される)

& mut を省略することもできます。
& mut が省略されたパターンの内側では、束縛する変数に ref mut が付いている場合と同じように、ミュータブル参照が変数に束縛されます。

1
2
3
// 省略しない場合
let &mut (ref mut a, ref mut b) = &mut (1, 2);
*a = 1;
1
2
3
// 省略した場合
let (a, b) = &mut (1, 2);
*a = 1;

& mut& の両方を省略したパターンの内側では ref mut ではなく、 ref が付いている場合と同じように、共有参照が変数に束縛されます。

1
2
// 省略しない場合
let &mut (_, &(ref x, ref y)) = &mut (1, &(2, 3));
1
2
// 省略した場合
let (_, (x, y)) = &mut (1, &(2, 3));

スライスパターン

Version 1.26.0 (2018-05-10) で追加されたパターンはもう一つあります。

それはスライスパターンです。
例を見てみましょう。

1
2
3
4
5
pub fn f(s: &[u32]) {    
if let &[x, y, z] = s {
// ここに処理を書く
}
}

このように、スライスに一致するパターンを書く事ができます。

また、固定長配列にも使用できます。
固定長配列の場合は配列長を考慮して必ずマッチするかどうかが判定されます。

1
2
3
fn f(s: [u32; 3]) {
let [x, y, z] = s; // このパターンは必ずマッチするので if let ではなく let を使用
}

一方、残念ながら Vec をそのままマッチさせることはできません。

1
2
3
4
let v = vec![1, 3, 3];
if let &[x, y, z] = &v {
// ここに処理を書く
}
1
2
3
4
5
6
7
error[E0529]: expected an array or slice, found `std::vec::Vec<{integer}>`
--> examples\slice_pattern.rs:3:13
|
2 | if let &[x, y, z] = &v {
| ^^^^^^^^^ pattern cannot match with input type `std::vec::Vec<{integer}>`

error: aborting due to previous error

スライスか固定長配列に変換してからマッチさせる必要があります。

1
2
3
4
let v = vec![1, 3, 3];
if let &[x, y, z] = &v[..] {
// ここに処理を書く
}

スライスの複数の要素にマッチする .. というパターンも提案されていますが、これは安定化されていません。
今後に期待しましょう。

1
2
3
4
5
6
#![feature(slice_patterns)]
fn f(s: &[u32]) {
if let &[head, ..] = s {
//
}
}

パターンの括弧

次の例を見てください。

1
2
let a = 20;
let &x = &a;

変数 x の前に & を付け、参照外しをしています。

次に、x に他の値を代入したくなったとします。

1
2
3
let a = 20;
let &x = &a;
x = 4;
1
2
3
4
5
6
7
8
9
10
error[E0384]: cannot assign twice to immutable variable `x`
--> examples\parentheses.rs:3:5
|
2 | let &x = &a;
| -
| |
| first assignment to `x`
| help: make this binding mutable: `mut x`
3 | x = 4;
| ^^^^^ cannot assign twice to immutable variable

当然のことながら代入できませんね。 x はミュータブルではないので。
そこで x の前に mut を付けて代入できるようにしましょう。

1
2
3
let a = 20;
let &mut x = &a;
x = 4;
1
2
3
4
5
6
7
8
 --> examples\parentheses.rs:2:9
|
2 | let &mut x = &a;
| ^^^^^^ types differ in mutability
|
= note: expected type `&{integer}`
found type `&mut _`
= help: did you mean `mut x: &&{integer}`?

おや?ミュータブル参照の参照外し &mut x になってしまいました。

そうです。mut が二つの意味で使われているため、表現できないパターンが存在するのです。

というのは先日までの話。
Version 1.31.0 (2018-12-06) からはパターンで括弧が使えるようになり、この問題はなくなりました。

1
2
3
let a = 20;
let &(mut x) = &a;
x = 4;

これで共有参照の参照外しをしつつ、変数をミュータブルにすることができるようになりました。

.. を利用したprivateフィールドを含む構造体へのマッチ

構造体でパターンマッチを使用できるのはアクセス可能なフィールドだけです。

1
2
3
4
5
6
7
8
9
10
mod my_module {
pub struct S {
pub a: u32,
b: u32,
}
}
use crate::my_module::S;
fn f(s: S) {
let S { a, b } = s;
}
1
2
3
4
5
error[E0451]: field `b` of struct `my_module::S` is private
--> examples\private_field.rs:9:16
|
9 | let S { a, b } = s;
| ^ field `b` is private

このように S::b はprivateフィールドの為、モジュール外からは使用できません。

が・・・例外が1つだけあります。

指定されていない全てのフィールドを無視するパターン .. を使用すれば、privateフィールドを含む構造体にモジュール外からマッチさせることができます。

1
2
3
fn f(s: S) {
let S { a, .. } = s;
}

参考

Rustのリリースノート
Slice patterns - The Edition Guide
rust-lang/rfcs/text/2005-match-ergonomics.md

]]>
<p>本記事は<a href="https://qiita.com/advent-calendar/2018/rust2" target="_blank" rel="noopener">Rustその2 Advent Calendar 2018</a> の17日目の記事です。</p> <p>本blogで <a href="https://frozenlib.net/blog/2018-03-11_rust-pattern-match/">Rustのパターンマッチを完全に理解した</a> を書いてから約1年が経過しました。</p> <p>その間にRustにはパターンマッチ関連の機能もいくつか追加され、以前の内容ではパターンマッチを完全に理解したとは言えなくなってしまいました。</p> <p>そこで、本記事では</p> <ul> <li>Rustに最近追加されたパターンマッチ関連の仕様</li> <li>最近追加されたわけではないが、以前の記事で書き忘れた機能</li> </ul> <p>について紹介したいと思います。</p>
Rustのパターンマッチを完全に理解した https://frozenlib.net/blog/2018-03-11_rust-pattern-match/ 2018-03-11T12:00:00.000Z 2018-12-01T11:46:20.761Z <![CDATA[

プログラミング言語Rustのパターンマッチを完全に理解しました!!!
記念にここにメモしておこうと思います!

let文について

let文ってありますよね?こういうのです。

1
let x = 1;

タプルや構造体ならこんな感じ。

1
2
3
4
5
6
7
struct Foo {
a: i32,
b: i32,
};

let x = (1, 2);
let x = Foo { a: 1, b: 2 };

これが基本系ですね。

これが基本?そうじゃない!

let 変数名 = 式; と書くのが基本・・・そう思っていましたが、違ったんです。
変数名 ではなく 右辺の型 と同じ構造を書くのが基本、そう考えましょう。

つまり、タプルや構造体なら下のように。

1
2
3
4
5
6
7
struct Foo {
a: i32,
b: i32,
};

let (x, y) = (1, 2);
let Foo { a: x, b: y } = Foo { a: 1, b: 2 };

参照なら、右辺と同じように左辺にも&を付ける。

1
2
let &(x, y) = &(1, 2);
let &mut (x, y) = &mut (1, 2);

さらに・・・より複雑なら型なら同じように複雑に。

1
2
3
4
5
6
struct Bar {
a: (i32, i32),
b: i32,
};

let &Foo { a: (x, y), b: z } = &Foo { a: (1, 2), b: 3 };

右辺と同じ構造を左辺にも書く。それが基本と考えましょう。
こうやって書くと左辺の変数(x,y,z)に右辺の対応する値(1,2,3)が入ります。

値をまとめる

右辺では値を直接書かなくても変数で構造体やタプルを一度に指定できますよね?
こんな感じです。

1
2
3
4
5
6
7
8
struct Bar {
a: (i32, i32),
b: i32,
};

let l = (1, 2);

let &Foo { a: (x, y), b: z } = &Foo { a: l, b: 3 };

8行目の右辺で(1, 2)と書く代わりに、変数lを使用しました。

この記法は左辺でも使えます。

1
2
3
4
5
6
struct Bar {
a: (i32, i32),
b: i32,
};

let &Foo { a: x, b: z } = &Foo { a: (1, 2), b: 3 };

これでタプル (1, 2) がまとまって変数 x に入りました。

もっとまとめる

構造体もまとめてみましょう。

1
2
3
4
5
6
struct Bar {
a: (i32, i32),
b: i32,
};

let &x = &Foo { a: (1, 2), b: 3 };

これで構造体がまるごと変数xに入りました。

もっともっとまとめる

参照もまとめてしまいましょう。

1
2
3
4
5
6
struct Bar {
a: (i32, i32),
b: i32,
};

let x = &Foo { a: (1, 2), b: 3 };

これでxに構造体の参照が入りました。

おや・・・このパターンは左辺が変数名だけ・・・最初に基本形だと思っていたパターンですね。
そうなんです、基本形だと思っていたパターンは値をまとめ尽くした最終形態だったのです。

forループ

パターンが使えるのはlet文だけではありません。forループでも同じようにパターンが使えます。
少し例を見てみましょう。

1
2
3
4
5
let xs = &[(1, 2), (3, 4), (5, 6)];

for &(a, b) in xs {
println!("{}", a); // 1, 3, 5
}

xsIntoIterator<&(i32,i32)> なので &(i32,i32) のパターン &(a, b) を使っています。これが基本と考えます。
そして、必要に応じて値をまとめたり、&を付けずに参照そのまま束縛して使いましょう。

束縛方法を変える

もう一つ例を見てみましょう。
&(String, i32) 型の式をそのままパターンにしてみます。

1
2
3
let s = &(String::from("A"), 1);

let &(a, b) = s;

1
2
3
4
5
6
7
8
error[E0507]: cannot move out of borrowed content
--> src\main.rs:4:9
|
4 | let &(a, b) = s;
| ^^-^^^^
| | |
| | hint: to prevent move, use `ref a` or `ref mut a`
| cannot move out of borrowed content

コンパイルエラーとなってしまいました。何故でしょうか?

まずは a の型が何になるか考えてみてください。
右辺の型は &(String, i32) なので、&(a, b)aString になりますよね?
これってどう見ても無理じゃないですか。所有権的に考えて。
String を作るには所有権を奪わないといけないけれど &(String, i32) の所有権は借り物で奪えない。

このような場合はパターンは使えないのでしょうか?
いいえ、そんなことはありません。

ref - 参照を束縛する

こんな時に使えるのが ref です。
変数名に ref を付けると、値そのものを束縛するのではなく、値の参照を束縛することができるのです。

試してみましょう。

1
2
3
let s = &(String::from("A"), 1);

let &(ref a, b) = s;

aの型は &String となり、コンパイルエラーも出なくなりました。

なお、ミュータブル参照を取得するには ref mut を使います。

1
2
3
let s = &mut (String::from("A"), 1);

let &mut (ref mut a, b) = s;

_ - 束縛しない

先ほどは参照を束縛しましたが、そもそも束縛しないという選択肢もあります。
その為に使えるのが _ です。

試してみましょう。

1
2
3
let s = &(String::from("A"), 1);

let &(_, b) = s;

束縛しないので所有権は必要なく、コンパイルエラーも出ません。

なお、所有権を奪わないのでこんなこともできます。

1
2
3
4
5
6
7
8
9
let s = String::from("A");

let _ = s;
let _ = s; // 運が良かったな。普通の変数ならここでエラーになっていた所だ。
let _ = s;
let _ = s;
let _ = s;
let _ = s;
let _ = s;

コピー不可能な型の値を何度でも代入することができます。意味はありませんが。

また、変数への代入のような値の寿命を延ばしてdropのタイミングを遅らせる効果もありません。
次のコードを実行してみましょう。

1
2
3
4
5
6
7
8
9
struct Foo(u32);
impl Drop for Foo {
fn drop(&mut self) {
println!("drop {}", self.0);
}
}
let _ = Foo(1); // Foo(1) はすぐに破棄される
let x = Foo(2); // Foo(2) はxのスコープが終了するときに破棄される
println!("3");

すると・・・結果は次のようになります。

1
2
3
drop 1
3
drop 2

お判りいただけたでしょうか?
Foo(1) の戻り値は _ に代入されましたが、スコープの終了を待たず、すぐにdropされています。

変数を使わないからといって変数を _ に変更すると、プログラムの意味が変わってしまう場合があるので注意が必要です。

変数未使用の警告を消したいならば、_x のようなアンダーバーから始まる変数名を使用することで警告を回避できます。

1
let _x = 1; // これなら変数未使用の警告は出ない

.. - 複数の値を束縛しない

_ を使えば必要な値だけを束縛でき無駄がなくて良いですね。
しかし、不必要な値がたくさんあった場合はどうでしょう? _ を沢山書くのは面倒です。

そんな時に役立つのが .. です。.. を使うと使わないフィールドを省略することができます。

1
2
3
4
5
6
7
8
9
10
11
struct Foo {
a: String,
b: i32,
c: i32,
}
let x = Foo {
a: String::from("A"),
b: 1,
c: 2,
};
let &Foo { b: b, .. } = &x;

今回は Foo::b だけ束縛してみました。

.. はタプルでも使えます。

1
2
3
4
5
6
let x = (1, 2, 3, 4);

let (a, ..) = x;
let (.., d) = x;
let (a, .., d) = x;
let (..) = x;

一致するとは限らないパターン

列挙型

今まで構造体、タプル、参照のパターンを見てきました。
次は列挙型のパターンを見てみましょう。

選択肢が一つの列挙型ならば、構造体と同じようにlet文で分解できます。

1
2
3
4
5
6
enum Foo {
A(u32),
};
let a = Foo::A(0);
let Foo::A(y) = a;
println!("{}", y); // 0

しかし。選択肢が複数の列挙型では・・・

1
2
3
4
5
6
enum Foo {
A(u32),
B(u32),
};
let a = Foo::A(0);
let Foo::A(y) = a;

1
2
3
4
5
error[E0005]: refutable pattern in local binding: `B(_)` not covered
--> src\main.rs:7:9
|
7 | let Foo::A(y) = a;
| ^^^^^^^^^ pattern `B(_)` not covered

コンパイルエラーが出てしまいました。何故でしょうか?

それは、右辺が左辺のパターンにマッチしない可能性があるからです。

右辺の型はFoo型なので Foo::A Foo::B の可能性があります。
しかし、左辺のパターンはFoo::A(y)・・・つまり、Foo::B の場合の処理が不可能なのです。
そのため、コンパイルエラーとなってしまったわけですね。

if let 式

列挙型のようなマッチしない可能性のあるパターンを使えるのが if let 式 です。

例を見てみましょう。

1
2
3
4
5
6
7
8
9
10
11
12
enum Foo {
A(u32),
B(u32),
};
let a = Foo::A(0);

if let Foo::A(y) = a {
// ここは実行される
}
if let Foo::B(y) = a {
// ここは実行されない
}

if let 式 はパターンにマッチした時だけ値を変数に束縛し、ブロックを実行するのです。
まさに if letの名にふさわしい機能ですね。

定数とのマッチ

さて「確実にマッチするパターン」 だけでなく 「マッチしないかもしれないパターン」 というものが存在することがわかりました。

ここでは「マッチしないかもしれないパターン」 でのみ使える構文をいくつか紹介しましょう。
まず始めは 1 2 3 のような 定数 です。

左辺に 1 2 3 のような定数を記述すると、その値が右辺の対応する位置の値と一致するときのみマッチするパターンになります。

1
2
3
4
5
6
if let (1, x) = (1, 1) {
// ここは実行される
}
if let (1, x) = (2, 1) {
// ここは実行されない
}

数字以外に文字列も使用できます。

1
2
3
4
5
6
if let ("abc", x) = ("abc", 1) {
println!("A");
}
if let ("xyz", x) = ("abc", 1) {
println!("X");
}

値の範囲とのマッチ

次に紹介するのは値の範囲です。
1...5 のように記述することで、特定の値の範囲と一致する場合のみマッチするパターンになります。

1
2
3
4
5
6
7
8
9
if let (1...5, x) = (1, 1) {
// ここは実行される
}
if let (1...5, x) = (5, 1) {
// ここは実行される
}
if let (1...5, x) = (7, 1) {
// ここは実行されない
}

値の範囲のイテレータを作成する構文(例:1..5) と異なり、

  • ピリオドの数が3つ
  • 最後の値を含む(上の例では5を含む)

事に注意してください。

@パターン

値の範囲とのマッチができるようになったのは良いのですが・・・ちょっと次の例を見てください。

1
2
3
4
5
fn foo(n: Option<i32>) {
if let Some(0...5) = n {
// ここに処理を書く
}
}

0から5までのうち、どの数字にマッチしたのかがわかりませんね。
いや n.unwrap() と書けば確かに数字は取り出せますよ? でも unwrap は使いどころを間違えてもコンパイラが警告してくれないので、できれば避けたいところです。

そんな時に役立つのが @ です。 変数名 @ パターン と書く事で、そのパターンにマッチした値を変数に束縛することができます。

1
2
3
4
5
fn foo(n: Option<i32>) {
if let Some(i @ 0...5) = n {
println!("{}", i);
}
}

これで値のチェックと束縛が同時に行えるようになりましたね。

else if let 節, else if 節, else 節

if let 式 には else if let 節, else if 節, else 節 を追加することができます。
使い方はその名前から想像できると思うので省略します。
気になる方はこのページの最後を見てください。

while let 式

条件式のパターンとマッチする間、何度もマッチとブロックの実行を繰り返す while let 式 もあります。
Iterator を実装しなくても Iterator::next 相当の関数だけで for 相当のループが書けるので便利です。
次の例はVecをスタックと見立てて、スタックが空になるまでループを回しています。

1
2
3
4
5
fn foo(q: Vec<u8>) {
while let Some(item) = q.pop() {
// ここに処理を書く
}
}

match式

先ほどの列挙型は選択肢が二つだけでした。
今度はもっと選択肢の多い列挙型で if let を使うことを考えてみましょう。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
enum Foo {
A,
B,
C,
}

fn bar(x: Foo) {
if let Foo::A = x {
// xがAのとき
} else if let Foo::B = x {
// xがBのとき
} else if let Foo::C = x {
// xがCのとき
}
}

う~ん、なんだか記述が冗長ですね。他の言語の switch 文のような物はないのでしょうか?

はぁい!ありまぁす!
Rustには match 式がありまぁす!

上のコードは match 式を使うと次のように書くことができます。

1
2
3
4
5
6
7
8
9
10
11
12
13
enum Foo {
A,
B,
C,
}

fn bar(x: Foo) {
match x {
Foo::A => { /* xがAのとき */ }
Foo::B => { /* xがBのとき */ }
Foo::C => { /* xがCのとき */ }
}
}

さて、match 式には if let 式と異なる重要な特性があります。
それは、いずれかのパターンに必ずマッチしなければならない という点です。

試しに、列挙型の選択肢を1つ増やしてみましょう。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
enum Foo {
A,
B,
C,
D,
}

fn bar(x: Foo) {
match x {
Foo::A => { /* xがAのとき */ }
Foo::B => { /* xがBのとき */ }
Foo::C => { /* xがCのとき */ }
}
}
1
2
3
4
5
error[E0004]: non-exhaustive patterns: `D` not covered
--> src\main.rs:11:9
|
11 | match x {
| ^ pattern `D` not covered

コンパイルエラーになってしまいました。

エラーメッセージに pattern `D` not covered とありますね。
そうです。この match 式では追加した Foo::D にマッチしないため、100%マッチする状況ではなくなり、エラーとなってしまったのです。

では、match 式に足りない Foo::D を追加してましょう。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
enum Foo {
A,
B,
C,
D,
}

fn bar(x: Foo) {
match x {
Foo::A => { /* xがAのとき */ }
Foo::B => { /* xがBのとき */ }
Foo::C => { /* xがCのとき */ }
Foo::D => { /* xがDのとき */ }
}
}

無事コンパイルエラーはなくなりました。

しかし、常に match 式ですべての選択肢を使うとは限りません。
一部の選択肢を省略することもできます。

100%確実にマッチさせなければならない・・・ならば、100%確実にマッチするパターンを書いてしまえばよいのです。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#[derive(Debug)]
enum Foo {
A,
B,
C,
D,
}

fn bar(x: Foo) {
match x {
Foo::A => println!("AAAAAAAAAA!!!"),
y => println!("x は {:?} です", y),
}
}

let y = x;y が100%確実にマッチするパターンだったことを思い出してください。
ここでも同じように y は100%確実にマッチするため、 match 式全体でも100%確実にマッチするようになり、エラーは出なくなります。

ところで、上の例では Foo::A の時は

println!("AAAAAAAAAA!!!") 
println!("x は {:?} です", y)

の両方が実行されるのでしょうか?それとも、前者のみが実行されるのでしょうか?

答えは、前者のみが実行される です。

match 式では最初にマッチしたパターンのブランチのみが実行されるため、100%マッチするパターンを最後に書くことで、他のブランチにマッチしなかった場合に実行するブランチになります。
他の言語の switchdefault のように使えますね。

なお、変数が必要なければ、代わりに _ を使うこともできます。

1
2
3
4
5
6
7
8
9
10
11
12
13
enum Foo {
A,
B,
C,
D,
}

fn bar(x: Foo) {
match x {
Foo::A => println!("AAAAAAAAAA!!!"),
_ => println!("xの値は教えぬ!"),
}
}

match式だけで使える構文

if let 式で使えるパターンの構文は全て match 式でも使うことができます。
一方、match 式だけで使えて、if let 式では使えない構文がいくつかあります。

複数のパターンを指定

| で区切って複数のパターンを記述することで、いずれかのパターンにマッチする場合に実行されるブランチになります。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
enum Foo {
A,
B,
C,
D,
}

fn bar(x: Foo) {
match x {
Foo::A | Foo::B => println!("A又はB"),
Foo::C | Foo::D => {
println!("C!");
println!("いや、Dかも!");
}
}
}

ガード

パターンの後に if 条件式 を付けることで、条件式に一致した場合のみ実行されるブランチになります。

1
2
3
4
5
6
7
8
fn bar(x: u32, y: u32) {
match (x, y) {
(vx @ 0...10, vy @ 0...10) if vx != vy => {
println!("xとyが10以下。ただしx==yの時は除く。")
}
_ => {}
}
}

まとめ

今まで色々なパターンを見てきました。まとめると・・・

  • パターンは値の束縛、分解、マッチングを同時に行う構文である
  • パターンは値を構築するときと同じ構文が使える
  • 100%マッチするパターンとマッチしない可能性があるパターンがあり、使える場面が異なる

理解すると実にシンプルですね。

なお、Rustの公式ドキュメントの第二版にパターンの詳しい説明があります。(英語)
ぜひこちらも見てください。
https://doc.rust-lang.org/book/second-edition/ch18-00-patterns.html

第一版の物足りない説明と比べて大幅に進化しています。

最後にパターンの構文と使える場所をまとめておきたいと思います。

パターンの構文一覧

構文の例名前
x変数への束縛
(x, y, z)タプル
Foo { a: x, b: y, c: z }構造体
Foo(x, y, z)タプル構造体
Foo::Aenum
_無視
..タプル・構造体の一部を無視
&x共有参照の展開
&mut xミュータブル参照の展開
ref x共有参照の束縛
ref mut xミュータブル参照の束縛
5定数
2...7値の範囲
x @ 2...7@による変数の束縛
p | p複数パターン (match 式のみ)
x if x > 5ガード (match 式のみ)

パターンの使える場所

値を変数に束縛できる場所で使えます。
具体的には、次の表の例で p と書かれた部分で使えます。
網羅性が 必要 となっている部分では100%マッチするパターンを書く必要があります。

場所複数パターン網羅性
let文let p = a;×必要
関数の仮引数fn (p : t) { }×必要
クロージャの仮引数|p| { }×必要
for ループfor p in expr { }×必要
while let ループwhile let p = expr { }×不要
if let 式if let p = expr { }〇 (*1)不要
match 式match expr { p => { }, p => { } }必要 (*2)
  • *1 : else if let 節を追加することで複数パターンを利用可能
  • *2 : 複数パターンの合計で網羅性が必要
]]>
<p><a href="https://www.rust-lang.org/ja-JP/" target="_blank" rel="noopener">プログラミング言語Rust</a>のパターンマッチを完全に理解しました!!!<br>記念にここにメモしておこうと思います!</p> <h2 id="let文について"><a href="#let文について" class="headerlink" title="let文について"></a>let文について</h2><p>let文ってありますよね?こういうのです。</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> x = <span class="number">1</span>;</span><br></pre></td></tr></table></figure> <p>タプルや構造体ならこんな感じ。</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">Foo</span></span> &#123;</span><br><span class="line"> a: <span class="built_in">i32</span>,</span><br><span class="line"> b: <span class="built_in">i32</span>,</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> x = (<span class="number">1</span>, <span class="number">2</span>);</span><br><span class="line"><span class="keyword">let</span> x = Foo &#123; a: <span class="number">1</span>, b: <span class="number">2</span> &#125;;</span><br></pre></td></tr></table></figure> <p>これが基本系ですね。</p> <h2 id="これが基本?そうじゃない!"><a href="#これが基本?そうじゃない!" class="headerlink" title="これが基本?そうじゃない!"></a>これが基本?そうじゃない!</h2>
差分表示ソフト Rekisa https://frozenlib.net/rekisa/ 2009-04-15T15:00:00.000Z 2018-06-14T11:04:54.524Z <![CDATA[

Rekisaは一度に3つ以上のファイルを比較できる、テキスト比較ソフトです。

主な特徴

  • キーボード操作に適したテキストエディタ風インターフェイス
  • 行単位の違いを背景色で表示し、文字単位の違いは下線で表示
  • 左右のテキストのカーソルが文字単位で連動する為、テキストの対応関係がわかりやすい
  • SHIFT_JIS、EUC-JP、UTF-8等、様々な文字コードに対応し、自動判別して開く
  • 様々なコマンドラインオプションが使用可能な為、他のソフトとの連携が容易
  • “svn.exe” を使用してSubversionと連携することが可能

基本情報

バージョン    : 0.32.018
動作環境     : Windows 2000、XP
必要なライブラリ : .NET Framework 1.1以上
開発環境     : Windows XP SP2 + Visual Studio 2003(.NET 1.1用)、2005(.NET 2.0用)
開発言語     : C# 1.1(.NET 1.1用)、2.0(.NET 2.0用)

ダウンロード

正式版 : ベクターから
開発版 : 下の開発版のバージョンをクリックしてください。

取扱説明書


開発版

.NET Framework 1.1 用 (取扱説明書あり)

.NET Framework 2.0 用 (取扱説明書なし)


更新履歴

ver 0.50.012 (2009/4/16)

  • バグ修正
    • U+2010の全角ハイフンが表示されない問題を修正
  • 機能追加
    • 現在のカーソル位置の文字番号を表示する機能を追加

ver 0.50.011 (2008/3/17)

  • バグ修正
    • 最後の連続する相違行内に左右のファイルで一致する文字が一文字もないと、その前の相違行で「次の相違点に移動」を実行した際にエラーが発生する問題を修正。

ver 0.50.010 (2007/12/13)

  • 機能追加
    • タイトルをフルパスで表示する設定を追加。

ver 0.50.009 (2007/12/13)

  • バグ修正
    • svn.exeを使用してファイルを開くと文字コード自動判別が行われない問題を修正。
      ショートカットキーの設定時にエラーが発生する問題を修正。
      ファイルを閉じた後に再度開くと、エラーが発生することがある問題を修正。

ver 0.50.008 (2007/12/4)

  • 性能向上
    • 文字コード自動判別精度の向上。(特にUTF-8のデータをSHIFT-JISと誤認識する場合に有効。)

ver 0.50.007 (2007/12/1)

  • バグ修正
    • 複数の文字コードが混ざったファイルが開けない問題を修正し、一部が文字化けした状態で開けるように。

ver 0.50.006 (2007/11/23)

  • 仕様変更
    • クリップボードにテキストをコピーした際の改行コードをLFからOS標準の改行コードに変更。

ver 0.50.005 (2007/9/20)

  • バグ修正
    • 既にRekisaが起動している状態でRekisaを起動すると、ウィンドウが前面に出てこないことがある問題を修正。
      文字コードの自動判別処理が正しく動作していなかった問題(ver 0.50.001以降)を修正。
  • 機能削除
    • 二重起動の高速化設定を削除。(常にONと同じ状態になります。)
  • 機能追加
    • 制限時間付き常駐機能を追加。
  • その他
    • 内部処理を大幅に変更。

ver 0.50.002 (2007/9/1)

  • バグ修正
    • 使用する .NET Framework のバージョンを2.0に変更。
  • 仕様変更
    • 文字コード自動判別を統計的な処理を使用したアルゴリズムに変更。
]]>
<p>Rekisaは一度に3つ以上のファイルを比較できる、テキスト比較ソフトです。 </p> <img src="/rekisa/screen_mini.png" class="img-right" title="[Rekisa画面イメージ]"> <h2 id="主な特徴"><a href="#主な特徴" class="headerlink" title="主な特徴"></a>主な特徴</h2><ul> <li>キーボード操作に適したテキストエディタ風インターフェイス</li> <li>行単位の違いを背景色で表示し、文字単位の違いは下線で表示</li> <li>左右のテキストのカーソルが文字単位で連動する為、テキストの対応関係がわかりやすい</li> <li>SHIFT_JIS、EUC-JP、UTF-8等、様々な文字コードに対応し、自動判別して開く</li> <li>様々なコマンドラインオプションが使用可能な為、他のソフトとの連携が容易</li> <li>“svn.exe” を使用してSubversionと連携することが可能</li> </ul>
コマンド型ランチャ O2Handler https://frozenlib.net/o2handler/ 2008-11-15T15:00:00.000Z 2018-02-14T12:47:27.139Z <![CDATA[

O2Handlerはキーワード入力によって、プログラムの実行、ファイルの削除、Web検索などを素早く行うためのソフトです。
次のような特徴があります。

  • ドラッグ&ドロップによる簡単コマンド登録
  • コマンドは自由に省略して実行可能(例:”InternetExplorer” を “ie” “inter” “net” の様にどの文字でも省略可能)
  • コマンドの合成機能を搭載(例:”Firefox<Vector” で FirefoxでVectorのページを開く)
  • 日本語のファイルやコマンドをローマ字で検索する機能を搭載(”メモ帳” を “memo” で検索可能)
  • ほぼ全ての操作は非同期処理(時間のかかる作業を行っても、同時に別の作業ができます)
  • タブ機能を搭載

基本情報

バージョン    : 0.04.032
公開日      : 2008/11/16
動作環境     : Windows XP SP2 (x86)、Windows Server 2003 R2(x86)、Windows Vista SP1 (x86、x64)
必要なライブラリ : .NET Framework 3.5 SP1以上

ダウンロード

正式版:正式版はまだ公開されていません。
開発版:

取扱説明書

作成中。


更新履歴

ver 0.04.032 (2008/11/16)

  • バグ修正
    • 特定のフォルダ(Windows Vistaの「コントロールパネル」-「プログラムと機能」など)で素早く再読込を行うとフリーズしてしまう問題を修正。
  • 仕様変更
    • 初期状態からShift + Backspaceを2回押したときに表示される内容を「使用したことのある機能のみ表示」から「全ての機能を表示」に変更。

ver 0.04.031 (2008/11/7)

  • バグ修正
    • タスクバーが画面下にあると「コマンド入力ウィンドウが画面からはみ出ないように自動的にウィンドウサイズを調整する機能」が正しく動作しない問題を修正。
      サンプルコマンド「Window一覧」「プロセス一覧」が動作しなくなっていた問題を修正。

ver 0.04.030 (2008/10/30)

  • バグ修正
    • O2Handlerから他のアプリケーションにファイルなどをドラッグ&ドロップしている途中で、ドロップ先アプリケーションが異常終了すると、O2Handlerも巻き込まれて異常終了してしまう問題を修正。
  • 性能向上
    • 検索速度の高速化。(コントロールパネルなどアクセスに時間がかかるフォルダで効果大。)

ver 0.04.029 (2008/9/23)

  • バグ修正
    • 選択中のタブ変更時にタブ一覧が表示されない問題を修正。

ver 0.04.028 (2008/9/5)

  • バグ修正
    • URI生成ウィザードでキーワードのみを入力すると異常終了するバグを修正。

ver 0.04.027 (2008/8/16)

  • バグ修正
    • メディアの入ってないフロッピーディスクドライブにアクセスするとダイアログが表示される問題を修正。

ver 0.04.026 (2008/8/10)

  • バグ修正
    • ファイル名の末端が全角スペースのファイルを正しく扱えない問題を修正。
]]>
<p>O2Handlerはキーワード入力によって、プログラムの実行、ファイルの削除、Web検索などを素早く行うためのソフトです。<br>次のような特徴があります。</p> <ul> <li>ドラッグ&ドロップによる簡単コマンド登録</li> <li>コマンドは自由に省略して実行可能(例:”InternetExplorer” を “ie” “inter” “net” の様にどの文字でも省略可能)</li> <li>コマンドの合成機能を搭載(例:”Firefox&lt;Vector” で FirefoxでVectorのページを開く)</li> <li>日本語のファイルやコマンドをローマ字で検索する機能を搭載(”メモ帳” を “memo” で検索可能)</li> <li>ほぼ全ての操作は非同期処理(時間のかかる作業を行っても、同時に別の作業ができます)</li> <li>タブ機能を搭載</li> </ul>
「ファイルを開く」 ダイアログ制御ソフト DialogHandler https://frozenlib.net/dialog_handler/ 2008-10-29T15:00:00.000Z 2018-02-14T11:15:41.091Z <![CDATA[

DialogHandlerは “ファイルを開く”、”フォルダの参照” ダイアログのファイル名をコマンドラインから制御するアプリケーションです。
ランチャと連携させることで、ファイルを開く際の操作が非常に楽になります。


基本情報

バージョン    : 1.0.014
最終更新日    : 2008/10/30
動作環境     : Windows XP SP2 (x86)、Windows Vista SP1 (x86、x64)
必要なライブラリ : (※バージョンによって異なる為、ダウンロードリンクの右に記載)

ダウンロード


更新履歴

ver 1.0.014 (2008/10/30)

  • 仕様変更
    • 動作環境の変更。(.NET Framework 3.5 SP1以上が必要)
  • 機能追加
    • .NET Framework 3.5を使用したアプリケーションの “ファイルを開く” ダイアログに対応。
      Windows Vistaのメモ帳やIEで使用されているファイル保存ダイアログの制御成功率を向上。

ver 1.0.013 (2008/10/8)

  • バグ修正
    • Windows Vistaのメモ帳のファイル保存ダイアログで、既存のファイル名が表示された状態だと正しく動作しない問題を修正。

ver 1.0.012 (2008/5/3)

  • 仕様変更
    • 32bit版のexeファイル名を変更。(DialogHandler.exe → DialogHandler.x86-32.exe)
  • 機能追加
    • 64bit版(DialogHandler.x86-64.exe)の追加。
      64bitアプリケーションの “フォルダの参照ダイアログ” に対応。(64bit版のみ)
  • 性能向上
    • Visual Studio 2008の参照追加ダイアログの制御成功率の向上。

ver 1.0.011 (2007/12/9)

  • 性能向上
    • Visual C# Express 2008の参照の追加ダイアログの制御成功率を向上。

ver 1.0.010 (2007/12/6)

  • バグ修正
    • Vistaのメモ帳などで使用されている “ファイルを開く” ダイアログと “ファイルの保存” ダイアログに対応。
]]>
<p>DialogHandlerは “ファイルを開く”、”フォルダの参照” ダイアログのファイル名をコマンドラインから制御するアプリケーションです。<br>ランチャと連携させることで、ファイルを開く際の操作が非常に楽になります。</p>
キーリピート高速化ソフト Hayate https://frozenlib.net/hayate/ 2008-08-15T15:00:00.000Z 2018-02-14T11:15:32.265Z <![CDATA[

Hayateはキーリピートを高速化するソフトです。
コントロールパネルで設定可能な限界値より速く設定することができます。


基本情報

バージョン    : 0.00.004
公開日      : 2008/8/16
動作環境     : Windows XP SP1、Vista SP1
必要なライブラリ : .NET Framework 3.5

ダウンロード

version 0.00.004
version 0.00.003
version 0.00.002


更新履歴

ver 0.00.004 (2008/8/16)

  • バグ修正
    • PCに負荷がかかるともう一度キーを押すまでキーリピートが止まらない事がある問題を修正。

ver 0.00.003 (2008/4/10)

  • バグ修正
    • 環境によってはキーリピートがほとんど速くならない問題を修正。
      突然キーリピートの加速が止まってしまう事がある問題を修正。
      HayateによるCPU使用率が高くなってしまう事がある問題を修正。

ver 0.00.002 (2008/4/9)

  • 仕様変更
    • 設定ツールの二重起動を抑止。
  • 性能向上
    • PCに負荷がかかるとキーリピート速度が遅くなる問題の軽減。

ver 0.00.001 (2008/4/5)

]]>
<p>Hayateはキーリピートを高速化するソフトです。<br>コントロールパネルで設定可能な限界値より速く設定することができます。</p>