Rust:Concise Control Flow with if let

Concise Control Flow with if let - The Rust Programming Language

if let記法は、あるパターンにマッチした時だけハンドリングし、残りは無視するような時に使用できる。

match

let config_max = Some(3u8);
match config_max {
    Some(max) => println!("The maximum is configured to be {max}"),
    _ => (),
}

if let

let config_max = Some(3u8);
if let Some(max) = config_max {
    println!("The maximum is configured to be {}", max);
}
  • パターンと式を=で分ける。
  • 上記の例では、パターンがSome(max)maxSomeの中の値がバインドされる。

if letを使うことで、タイピング量が減り、インデントも減り、定型コードも減るが、matchが強制していたチェックを失うことになる。どちらを使用するかはトレードオフの関係である。

if letelseも使用できる。

let mut count = 0;
if let Coin::Quarter(state) = coin {
    println!("State quarter from {:?}!", state);
} else {
    count += 1;
}

Rust:The match Control Flow Construct

The match Control Flow Construct - The Rust Programming Language

Rustにはmatchと呼ばれる、値をパターンに基づいて処理する強力な制御フロー構造がある。 パターンは、リテラル値、変数名、ワイルドカード、その他多くのもので構成することができる。
参照:Patterns and Matching

enum Coin {
    Penny,
    Nickel,
    Dime,
    Quarter,
}

fn value_in_cents(coin: Coin) -> u8 {
    match coin {
        Coin::Penny => {
            println!("Lucky penny!");
            1
        }
        Coin::Nickel => 5,
        Coin::Dime => 10,
        Coin::Quarter => 25,
    }
}
  • matchキーワードに続いて式を記述する。if式はboolean型に評価できる必要があるが、あらゆる型を取りうるところが大きな違い。
  • matcharmsは、パターン部分とコードで構成され、=>オペレーターがパターンとコードを分ける
  • armは,カンマで区切るが {}カーリーブラケットを使用した場合は、省略可能である
  • 各armに関連付けられたコードは式であり、match全体の戻り値となる

値に束縛されるパターン

match armsの有用な機能として、パターンにマッチした値を束縛できることである。

#[derive(Debug)] 
enum UsState {
    Alabama,
    Alaska,
    // --snip--
}

enum Coin {
    Penny,
    Nickel,
    Dime,
    Quarter(UsState),
}
fn value_in_cents(coin: Coin) -> u32 {
    match coin {
        Coin::Penny => 1,
        Coin::Nickel => 5,
        Coin::Dime => 10,
        Coin::Quarter(state) => {
            println!("State quarter from {:?}!", state);
            25
        }
    }
}

Optionによるマッチング

fn plus_one(x: Option<i32>) -> Option<i32> {
    match x {
        None => None,
        Some(i) => Some(i + 1),
    }
}

let five = Some(5);
let six = plus_one(five);
let none = plus_one(None);
  • Some(5)はSome(i)にマッチし、iにはSomeの値である5がバインドされ、新しいSome(i+5)が作成される

Matchは完全である

Rustは全てのケースをカバーしているかどうか検知する。

Catch-allパターンとアンダースコアプレースホルダー

let dice_roll = 9;
match dice_roll {
    3 => add_fancy_hat(),
    7 => remove_fancy_hat(),
    _ => (),
}

fn add_fancy_hat() {}
fn remove_fancy_hat() {}
  • それ以外にの値にマッチする場合はotherを使用する
  • パターンは順番に評価されるため、catch-all armsは最後に記述する必要がある
  • catch-allパターンで、その値を使用しない場合は、_を使用することができる
  • ユニット値(空のタプル) ()を指定することで、何もしない状態を表現できる

Rust:Defining an Enum

Defining an Enum - The Rust Programming Language

構造体は関連するフィールドとデータをグループ化する1つの方法である。
列挙型(enum)は取りうる値のセットを定義する。

enum IpAddrKind {
    V4,
    V6,
}

Enumの値

let four = IpAddrKind::V4;
let six = IpAddrKind::V6;

enumには直接データを持たせることができ、さらに値ごとに異なるデータを関連付けることが可能

enum IpAddr {
    V4(u8, u8, u8, u8),
    V6(String),
}

let home = IpAddr::V4(127, 0, 0, 1);
let loopback = IpAddr::V6(String::from("::1"));

標準ライブラリのIpAddrは、それぞれの値に異なる構造体を持たせている。

enumには様々なデータをもたせることが可能である。

enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
    ChangeColor(i32, i32, i32),
}
  • Qute:データなし
  • Move:構造体のような名前付きフィールド
  • Write:単一のString型
  • ChangeColor:3つの整数型

上記を構造体で表現した場合以下のようになるが、これらの全ての型に対応するような関数を定義することは簡単ではない。

struct QuitMessage; // unit struct
struct MoveMessage {
    x: i32,
    y: i32,
}
struct WriteMessage(String); // tuple struct
struct ChangeColorMessage(i32, i32, i32); // tuple struct

また、enumもメソッドを定義することができる。

impl Message {
    fn call(&self) {
        // method body would be defined here
    }
}

let m = Message::Write(String::from("hello"));
m.call();

Option型とNull値に対する優位性

Option型は標準ライブラリで定義されているenumで、何某かの値を持つか、何も無いかを一般化する。
Rustは他の多くの言語が持っているNullがなく、nullかnull以外かをOption<T>でコード化する。

enum Option<T> {
    None,
    Some(T),
}

Option<T>は通常のenumであり、Some(T)NoneOption<T>の取りうる値である。
<T>記法は、ジェネリック型引数で、今はSomeがあらゆる型のデータを1つ保持することができ、TマークはOption型全体として、実際の型に置き換わるとだけ理解しておけば良い。

    let some_number = Some(5);
    let some_char = Some('e');

    let absent_number: Option<i32> = None;
  • some_numberOption<i32>型で、some_charOption<char>型で異なる型になる。
  • RustはSomeに渡された実際の値で型を推測できるが、absent_numberは推測できないため、型を指定する必要がある
  • Someの場合は値があることを、Noneの場合は値がないことを表す。

何がnullよりよいのか?

Option<T>Tは異なる型になり、コンパイラOption<T>の値を確実に有効な値としては使用しない。

// i8とOption<i8>は異なる型のため、加算できないのでコンパイルエラーになる
 let x: i8 = 5;
 let y: Option<i8> = Some(5);
 let sum = x + y;

Rustではi8のような型の場合、コンパイラーが有効な値であることを保証してくれるため、nullチェックの必要がない。
Option<i8>のような型の場合のみ、値がない場合の考慮が必要で、そのようなケースをハンドリングしているかどうかをコンパイラーは確認する。

言い換えると、T型の処理をするには、Option<T>Tに変換する必要があり、その時、値がない場合のケースの処理も強制させるため、not nullエラーが発生しない。

Rust:Method Syntax

Method Syntax - The Rust Programming Language

メソッドは関数に似ているが、以下の点が異なる

  • 構造体、列挙型(enum)、トレイトオブジェクトの中に定義される
  • 第1パラメータは常にself:メソッドを呼び出すインスタンスである

メソッド定義

#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}

impl Rectangle {
    fn area(&self) -> u32 {
        self.width * self.height
    }
}

fn main() {
    let rect1 = Rectangle {
        width: 30,
        height: 50,
    };
    println!("{}", rect1.area());
}
  • メソッドの第1引数は、所有権を取るself、不変参照の&self、可変参照の&mut selfにすることができる
  • フィールド名と同じ名前のメソッドも可能。その場合、()の有無でメソッド呼び出しか否かをコンパイラーは判断する
  • フィールド名と同名のメソッドは、通常はgetterとして定義する。getterにすることでフィールドを読み取り専用にすることができる。

Where’s the -> Operator?

  • Rustには自動参照・参照外しという機能があり、メソッド呼び出しはこの振る舞いをする数少ない場所である
  • object.something()とすると、Rustは自動的に、&&mut、または*をつける
  • 以下は同じ動きをする
p1.distance(&p2);
(&p1).distance(&p2);

より多くのパラメータをもつメソッド

selfパラメータのあとに、複数のパラメータを付けることができ、関数と同様に動作する。

関連関数

implブロックの中に定義するが、selfパラメータを持たない関数のこと。
インスタンスを必要としないため、メソッドではない。例)String::from関数。
関連関数はコンストラクタとして使用されることが多い。また関数名にはnewが使われることが多いが、言語に組み込まれたものではない。

impl Rectangle {
    fn square(size: u32) -> Self {
        Self {
            width: size,
            height: size,
        }
    }
}
  • Selfキーワードはimplキーワードの関数内に現れると、戻り値の型を表す
  • 関連関数の呼び出しはlet sq = Rectangle::square(3);のように::を使用する

複数のimplブロック

複数のimplブロックを定義することが可能。

【今日の一曲】never young beach - 明るい未来

今日の一曲

Youtubeのおすすめで出てきた曲。
自分が知らない素敵な曲に出会えると、少しテンション上がる。

never young beach - 明るい未来 www.youtube.com

never young beach - Wikipedia

neveryoungbeach.jp