【Rust】がばがばRust独学 - 6. Enum / Pattern Matching
Rustの公式ドキュメントを通して、Rustを独学していく記事になります。(がば要素あり)
今回は、EnumとPattern mattingについて学びつつ、気になった点をコード確認した結果を記事にしています。
- versions
$ rustc --version rustc 1.40.0 (73528e339 2019-12-16)
Enum
基本的な使用方法
公式ドキュメント(doc.rust-lang.org)の例より、IPアドレスのv4, v6を考えた場合、IpAddrKind
により、v4, v6のenumを定義するとともに、 IpAddr
により、IPアドレスの制御をできるようなStructを定義することが可能です。debug出力をさせるために、 #[derive(Debug)]
を enum
、 struct
の両方につけています。
#[derive(Debug)] enum IpAddrKind { V4, V6, } #[derive(Debug)] struct IpAddr { kind: IpAddrKind, address: String, } fn main() { let home = IpAddr { kind: IpAddrKind::V4, address: String::from("127.0.0.1"), }; let loopback = IpAddr { kind: IpAddrKind::V6, address: String::from("::1"), }; println!("home -> {:#?}", home); println!("loopback -> {:#?}", loopback); }
home -> IpAddr { kind: V4, address: "127.0.0.1", } loopback -> IpAddr { kind: V6, address: "::1", }
enum variant
enumに対して、値を持たせることも可能です。
下記のように、v4の場合はIPアドレス、v6の場合はリダイレクト先などを設定したい場合は、V4(u8, u8, u8, u8)
や V6(String)
のように、tapleのように記述し、データを定義することが可能です。
#[derive(Debug)] enum IpAddr { V4(u8, u8, u8, u8), V6(String), } fn main() { let home = IpAddr::V4(127, 0, 0, 1); let loopback = IpAddr::V6(String::from("::1")); println!("home -> {:#?}", home); println!("loopback -> {:#?}", loopback); }
home -> V4( 127, 0, 0, 1, ) loopback -> V6( "::1", )
V4(u8, u8, u8, u8)
はStruct Ipv4Addr
が存在していた場合 V4(Ipv4Addr)
のように、列挙型(enum)のvariantとして構造体をもたせることも可能です。
Option
Rustにはnullは無いです。それは、 Option
により、値が有るか否かを判別することができるためです。
下記の例のように、値がある場合を Some
、ない場合は None
として制御するためです。
fn main() { let x = Some(5); let s = Some("a string"); let n: Option<i32> = None; println!("x -> {:#?}", x); println!("s -> {:#?}", s); println!("n -> {:#?}", n); }
x -> Some( 5, ) s -> Some( "a string", ) n -> None
公式ドキュメント(doc.rust-lang.org)引用の、nullの発明者であるTony Hoareの「Null References:The Billion Dollar Mistake」より、nullによる無数のエラーや脆弱性、システムクラッシュが発生していることによる損害を引き起こさないために、一般的なバグを防ぐ意味合いでOptionの利用がされている背景があります。
また、Optionは列挙型であるため、下記のように単純に計算はできません。しかしながら、 nullを許容する言語であった場合、もしx
がnullであるとすると、バグになってしまいます。
fn main() { let x: Option<u8> = Some(5); let y: u8 = 3; let z = x + y; }
error[E0369]: binary operation `+` cannot be applied to type `std::option::Option<u8>` --> demo.rs:5:12 | 5 | let z = x + y; | - ^ - u8 | | | std::option::Option<u8> | = note: an implementation of `std::ops::Add` might be missing for `std::option::Option<u8>
match
Option
を利用した単純な加算の場合、下記のように x
がSome/Noneによって計算を分けることが可能です。
fn main() { let x: Option<u8> = Some(5); let y: u8 = 3; let z = match x { None => y, Some(i) => i + y, }; println!("z -> {}", z); }
z -> 8
match
の網羅性
先程の match
から None
を除外した場合、下記のようにコンパイルエラーとなり、事前にエラーを防ぐことが可能です。
fn main() { let x: Option<u8> = Some(5); let y: u8 = 3; let z = match x { Some(i) => i + y, }; println!("z -> {}", z); }
error[E0004]: non-exhaustive patterns: `None` not covered --> demo.rs:5:16 | 5 | let z = match x { | ^ pattern `None` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
_
プレースホルダー
下記のように、Some
であれば対象の処理をするのような可能です。
fn main() { let x: Option<u8> = Some(5); let y: u8 = 3; let z = match x { None => y, Some(_) => 100 + y, }; println!("z -> {}", z); }
z -> 103
また、公式ドキュメント(The match Control Flow Operator - The Rust Programming Language)に記載されているデモコード(少し改変しています)のように、caseではないものの判別としても利用可能です。
fn main() { let some_u8_value = 1u8; match some_u8_value { 1 => println!("one"), 3 => println!("three"), 5 => println!("five"), 7 => println!("seven"), _ => (), } }
one
if let
公式ドキュメント(Concise Control Flow with if let - The Rust Programming Language)に記載されているデモコード(少し改変しています)のように、ただ Some(3)
であれば、 println!("three")
を処理するという式であった場合、冗長になってしまいます。
fn main() { let some_u8_value = Some(3u8); match some_u8_value { Some(3) => println!("three"), _ => (), } }
three
そのため、 if let
を利用することにより、網羅性を無くし、冗長的では無くすことが可能です。
fn main() { let some_u8_value = Some(3u8); if let Some(3) = some_u8_value { println!("three"); } }
three
単純に if
制御であるため、 else
else if
も利用可能です。
最後に
enum
や match
はScala本当にお世話になった存在です。DBで取ってきた値やweb APIから取ってきた値に対して、null許容のデータに対して処理させたりなど、有効活用ができるとても便利で堅牢性が高くなる構文です。とてもありがたい・・・
次はPackages, Crates, Modules, Paths辺りの理解を深めていきます。