Bitterless Rust

構造体と列挙型

構造体 (struct)

複数の値をひとまとめにしたもの.他の言語のクラスやオブジェクトに近い.

struct User {
    name: String,
    age: i32,
}

fn main() {
    let user = User {
        name: String::from("Alice"),
        age: 25,
    };

    println!("{} is {} years old", user.name, user.age);
}

メソッドを生やす (impl)

impl ブロックで構造体にメソッドを追加できる:

struct User {
    name: String,
    age: i32,
}

impl User {
    fn greet(&self) {
        println!("Hi, I'm {}!", self.name);
    }

    fn have_birthday(&mut self) {
        self.age += 1;
    }
}

fn main() {
    let mut user = User {
        name: String::from("Alice"),
        age: 25,
    };

    user.greet();          // Hi, I'm Alice!
    user.have_birthday();
    println!("{}", user.age); // 26
}

メソッドの第一引数は「自分自身」:

書き方 意味
&self 自分を読むだけ.変更しない
&mut self 自分を変更する

語弊注意: & は本来「参照」という重要な概念だけど,メソッドの &self&mut self はほぼおまじない.「読むだけ」か「変更もする」か,そのくらいの理解で OK.

関連関数(コンストラクタ的なやつ)

self を取らない関数も impl に書ける.User::new(...) のように :: で呼ぶ:

impl User {
    fn new(name: String, age: i32) -> User {
        User { name, age }
    }
}

fn main() {
    let user = User::new(String::from("Bob"), 30);
    user.greet();
}

User { name, age }User { name: name, age: age } の省略形.変数名とフィールド名が同じなら省略できる.

列挙型 (enum)

enum は「このどれか」を表す型:

enum Direction {
    Up,
    Down,
    Left,
    Right,
}

fn main() {
    let dir = Direction::Up;

    match dir {
        Direction::Up => println!("Going up!"),
        Direction::Down => println!("Going down!"),
        Direction::Left => println!("Going left!"),
        Direction::Right => println!("Going right!"),
    }
}

match と相性が抜群.すべてのバリアントを網羅しないとコンパイラに怒られるので,ケースの漏れを防げる.

データを持つ enum

enum の各バリアントにはデータを持たせられる.これが Rust の enum の真骨頂:

enum Shape {
    Circle(f64),           // 半径
    Rectangle(f64, f64),   // 幅, 高さ
}

fn area(shape: Shape) -> f64 {
    match shape {
        Shape::Circle(r) => 3.14 * r * r,
        Shape::Rectangle(w, h) => w * h,
    }
}

fn main() {
    let c = Shape::Circle(5.0);
    let r = Shape::Rectangle(3.0, 4.0);

    println!("Circle: {}", area(c));       // 78.5
    println!("Rectangle: {}", area(r));    // 12.0
}

match でバリアントを分岐しつつ,中のデータを取り出せる.

derive: おまじない

構造体や enum に #[derive(...)] を付けると便利な機能が自動で追加される:

#[derive(Debug, Clone, PartialEq)]
struct Point {
    x: f64,
    y: f64,
}

fn main() {
    let p = Point { x: 1.0, y: 2.0 };

    // Debug: {:?} で中身を表示できる
    println!("{:?}", p); // Point { x: 1.0, y: 2.0 }

    // Clone: .clone() でコピーできる
    let p2 = p.clone();

    // PartialEq: == で比較できる
    println!("{}", p == p2); // true
}
derive 何ができるようになるか
Debug {:?} で中身を表示
Clone .clone() でコピー
PartialEq == で比較

とりあえずこの 3 つをセットで付けておけば困らない.

enum にも同じように使える:

#[derive(Debug, Clone, PartialEq)]
enum Color {
    Red,
    Green,
    Blue,
}

fn main() {
    let c = Color::Red;
    println!("{:?}", c); // Red
}

これで自分のデータ型を作れるようになった.次は Vec や String といった「動的なやつら」と,Rust 特有の「所有権っぽい何か」に触れる.

本に戻る