11日目です。文法もちょくちょくやらないとソースが読めないので列挙型の話です。
列挙型
enumとも言います。他の言語でも名前付き定数の集合を定義するためによく使われています。 例えば信号機型ならこんな感じ
enum Signal { Red, Yellow, Green }
Red, Yellow, Greenはvariantもしくは構成子と言います。
識別子には定数値を指定できます。
enum HttpStatus { Ok = 200, Created = 201, NotFound = 404, }
指定しないと0から順に割り振られるので、信号機の例だとSignal::Red
は0, Yellow
は1のようになります。
列挙型の構成子から整数型へキャストできますが、その逆はできません。
let a = Signal::Red as i32; assert_eq!(a, 0);
構造体のようにメソッドも書くことができます。
enum HttpStatus { Ok = 200, Created = 201, NotFound = 404, } impl HttpStatus { fn did_succeeded(code: HttpStatus) -> bool { let code = code as i32; return 200 <= code && code < 300; } }
tuple variants
struct Image { size: ImageSize, url: String } enum ImageSize { Big(usize, usize), Small(usize, usize) }
ImageSize
の構成子は引数を持っており、これらの構成子はtuple variantsと呼ばれます。タプル型構造体を構成子として持つような感じでしょうか。
let big_image = Image{size: ImageSize::Big(2000, 3000), url: "http://hogebar/image.jpg".to_string()};
コンストラクタで値を渡すことにより様々な構成子の値を持つImageSize
を作成できます。
struct variants
構造体自体も構成子として持つことができます。
enum Figure { Circle { center: Point2d, radius: f32 }, Triangle { corner1: Point2d, corner2: Point2d, corner3: Point2d } }
Option, Result
そういえば今までよく使ってきたOption<T>
やResult<T, E>
もenum型でした。TやEのような型変数をもつenumもあります。これらの構成子となっているSome(T)
やOk(T)
などは先ほどのtuple variantsです。
enum Option<T> { None, Some(T) } enum Result<T, E> { Ok(T), Err(E) }
値を取り出す
列挙型の中のデータにアクセスするには、パターンマッチを利用します。
先ほどのImage
の例を使います。
struct Image { size: ImageSize, url: String } enum ImageSize { Big(usize, usize), Small(usize, usize) } fn main() { let mut big_image = Image{size: ImageSize::Big(2000, 3000), url: "http://hogebar/image.jpg".to_string()}; }
画像サイズを説明する文字列を返す機能が欲しければ以下のようなメソッドを追加します。パターンマッチする部分をwidth
, height
のようなローカル変数に代入して取り出しています。
impl Image { fn get_size_description(&self) -> String { match self.size { ImageSize::Big(width, height) => format!("Big size => width: {}, height: {}", width, height), ImageSize::Small(width, height) => format!("Small size => width: {}, height: {}", width, height) } } } fn main() { let mut big_image = Image{size: ImageSize::Big(2000, 3000), url: "http://hogebar/image.jpg".to_string()}; println!("{}", big_image.get_size_description()); }
実行結果
Big size => width: 2000, height: 3000
参照を取り出す
match result { Ok(ref mut val) => { handle(val); // &mut を渡す }, Err(ref err) => log_error(err), }
範囲を取り出す
..=
を使う.範囲式の..
が右開区間であるのに対し,閉区間になる...
はまだ実験的にしか使えないらしい.
match c { '0' ..= '9' | 'a' ..= 'z' => { // process }, _ => process(), }
パターンガード
match
の分岐にif
を書くことができる.
match val { Some(m) if m < 15 => { println!("true"); }, _ => println!("false") }
マッチ全体を取り出す
@
パターンを使う
let n = Some(2); match n { l @ Some(_) => { println!("true: {:?}", l); }, _ => println!("false") }