はじめての Rust 入門 Part4 ~構造体について学ぶまで~

はじめての Rust 入門 Part4 ~構造体について学ぶまで~

link です。

高速でセキュリティ的にも安全な言語として Rust が注目を集めています。

今回はそんな Rust の勉強をしていきます。

本記事は Part3 の続きになっています。

想定環境

  • Windows 11
  • Rust 1.72

Rust の構造体について

基本の構造体

構造体は struct で定義されます。

内部ではフィールドと呼ばれるデータ片の名前と型を定義します。

以下のコードはユーザーアカウントに関する情報を保持する構造体です。

基本の構造体
struct User {
    username: String,
    email: String,
    sign_in_count: u64,
    active: bool,
}

構造体を使用するには、各フィールドに対して具体的な値を指定した構造体のインスタンスを生成します。

インスタンスはフィールド名とそのフィールドに格納したいデータを指定することで生成できます。

フィールドは構造体で宣言した通りの順番に指定する必要はありません。

たとえば、以下のコードのように特定のユーザーを宣言できます。

インスタンスの生成
let user1 = User {
    email: String::from("someone@example.com"),
    username: String::from("username123"),
    active: true,
    sign_in_count: 1,
};

構造体からフィールドの値を取得するにはドット記法を使います。

たとえば、以下のコードのインスタンスで Useremailだけが欲しいなら、 user1.email で取得できます。

インスタンスが可変であればドット記法を使い、特定のフィールドに代入することで値を変更できます。

フィールドの取得・代入
let mut user1 = User {
    email: String::from("someone@example.com"),
    username: String::from("username123"),
    active: true,
    sign_in_count: 1,
};

user1.email = String::from("anotheremail@example.com");

インスタンス自体を可変にするため、一部のフィールドのみを可変にはできません。

また、構造体の新規インスタンスを関数本体の最後の式として、そのインスタンスを返すことができます。

インスタンスを返す関数
fn build_user(email: String, username: String) -> User {
    User {
        email: email,
        username: username,
        active: true,
        sign_in_count: 1,
    }
}

フィールド初期化省略記法

JavaScript のオブジェクト生成と同じように、関数の仮引数名と構造体のフィールド名がまったく同じな場合、フィールド初期化省略記法が使えます。

以下のコードのように emailusername を繰り返し記述する必要がなくなります。

フィールド初期化省略記法
fn build_user(email: String, username: String) -> User {
    User {
        email,
        username,
        active: true,
        sign_in_count: 1,
    }
}

構造体更新記法

JavaScript のオブジェクト生成と同じように、前のインスタンスの値を使用しつつ、変更する箇所もある形で新しいインスタンスを生成できます。

まず、更新記法なしの場合で新しい User インスタンスを生成する方法を示します。

emailusername には新しい値をセットしていますが、それ以外には user1 の値を使用しています。

構造体更新記法なし
let user1 = User {
    email: String::from("someone@example.com"),
    username: String::from("username123"),
    active: true,
    sign_in_count: 1,
};

let user2 = User {
    email: String::from("another@example.com"),
    username: String::from("anotherusername567"),
    active: user1.active,
    sign_in_count: user1.sign_in_count,
};

.. という記法で構造体更新記法を使用すると、明示的にセットされていない残りのフィールドが、与えられたインスタンスのフィールドと同じ値になるように指定します。

構造体更新記法あり
let user2 = User {
    email: String::from("another@example.com"),
    username: String::from("anotherusername567"),
    ..user1
};

Rust でのオブジェクト指向プログラミング

Rust には構造体はありますが、クラスはありません。

しかし、 Rust でのオブジェクト指向プログラミングを可能にするための機能がいくつか実装されています。

メソッド記法

メソッドは impl ブロック上で構造体に対して定義されます。

最初の引数は必ず self になり、これはメソッドが呼び出されている構造体インスタンスを表します。

以下のコードでは Rectangle 構造体上に area メソッドを定義しています。

メソッド記法
struct Rectangle {
    width: u32,
    height: u32,
}

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

    fn can_hold(&self, other: &Rectangle) -> bool {
        self.width > other.width && self.height > other.height
    }
}

fn main() {
    let rect1 = Rectangle { width: 30, height: 50 };

    println!("The area of the rectangle is {} square pixels.", rect1.area());
}

関連関数

impl ブロック内に self を引数に取らない関数を定義できます。

これは構造体に関連付けられているため、関連関数と呼ばれます。

関連関数は関数であり、メソッドではありません。というのも、対象となる構造体のインスタンスが存在しないからです。これまで利用してきた String::from が関連関数にあたります。

関連関数は、構造体の新規インスタンスを返すコンストラクターによく使用されます。

たとえば、以下のコードのように、正方形の Rectangle を生成できます。

正方形を生成する関連関数
impl Rectangle {
    fn square(size: u32) -> Rectangle {
        Rectangle { width: size, height: size }
    }
}

関連関数を呼び出すためには、 String::from のように構造体名と一緒に :: 記法を使用します。

複数の impl ブロック

複数の impl ブロックを存在させることができます。

複数のimplブロック
impl Rectangle {
    fn area(&self) -> u32 {
        self.width * self.height
    }
}

impl Rectangle {
    fn can_hold(&self, other: &Rectangle) -> bool {
        self.width > other.width && self.height > other.height
    }
}

複数の impl ブロックが有用になるケースについてはのちほど学ぶトレイトで触れます。

参考サイト

まとめ

今回は Rust の構造体について勉強しました。

次回は Rust の列挙型について勉強していきます。

それではまた、別の記事でお会いしましょう。

linkohta