BackEnd

Rust入門してみた (構造体 / トレイト)

投稿日:

はじめに

前回に引き続き、Rustの入門編です。今回は構造体とトレイトについて触れます。

構造体

Rustの構造体について見ていきます。

構造体の宣言は struct キーワードで始まり、カッコの中に、フィールド名と型を定義していきます。
インスタンスを作成するには、 User {} という形で、カッコ内にフィールド名と値を指定します。
構造体も変数と同じように、デフォルトでは不変であり、フィールドの中身を書き換えることもできません。フィールドの中身を書き換えるには、構造体の変数宣言時に mut キーワードを使用します。

ちなみに、不変のまま構造体の値を更新するには、構造体のインスタンス自体を作り直すことになりますが、その時に「構造体更新記法」を使用すると、コードを省略することができます。

メソッド

構造体にメソッドを定義するには、構造体を定義した後 impl User {} という形で、カッコの中に関数と似たシンタックスで定義していきます。まずは、コードを例示します。

各メソッドの第一仮引数には &self とします。このselfを経由して、自身のフィールドにアクセスすることができます。
また、自身のフィールドを変更する場合は &mut self とします。その場合、構造体そのものの変数定義時に、mutキーワードを使用する必要があります。

self として、所有権を奪うこともできます。

この場合、メソッドを呼び出すと呼び出すと所有権が移ってしまうため、その後インスタンスを再利用することはできません。
回避方法としては、関数の時と同じで、メソッドで return self することで所有権を返すか、インスタンスをcloneしてからメソッドを呼ぶことが考えられます。

関連メソッド

selfにアクセスする必要のない「関連メソッド」を定義することもできます。例えば、以下のようなものです。

Rustでの慣習で、自身のインスタンスを生成する関数は、 fn new() というように、関連メソッドで定義します。関連メソッドの呼び出しは、 User::new() という形で呼び出します。

トレイト

Rustでのトレイトは次のように trait キーワードを使って定義できます。先ほどのコードを変更してみます。

トレイトを宣言するには trait キーワードを使用し、かっこの中にメソッドを定義します。構造体がトレイトを実装するには、 impl トレイト名 for 構造体名 とし、かっこの中にトレイトで定義されているメソッドを実装します。

また、引数として受ける場合にトレイトを使用する場合には、 &impl トレイト名 として受けます。

このようにトレイトを使用することで、ポリモーフィズムを実現することができます。

構造体のフィールドの1つとして、トレイトのインスタンスを持つ場合

構造体のフィールドの1つとして、トレイトのインスタンスを持つ場合 dyn キーワードを使用します。また、合わせてBoxを使う必要があります。

Rustの構造体は、基本的にはコンパイル時に必要なデータサイズを計算できるようになっている必要があります。ですが、トレイトの場合は、実際に注入されるインスタンスの実装によって必要なデータサイズが異なるため、コンパイル時には必要なメモリ量を知ることができません。

そこで使用するのが、ヒープです。ヒープは、可変長データを扱うためのメモリ領域で、Boxを使うとヒープに実データを格納することができます。Box以外にも、VecやStringといった可変長データもヒープを使用します。

なぜ、コンパイル時に構造体に必要なデータサイズを知って置ける必要があるのでしょうか。それは、Rustでは基本的にスタック領域を使用するためです。スタック領域は、固定長のデータを扱うことができる代わりに、動作が高速です。
また、所有権システムを使用しているために、コンパイル時にスタックのデータ容量を計算することが可能になるのです。

対して、ヒープは可変長である代わりに、動作が低速です。そのため、必要な時にだけ使用するようにします。

Box自体は所有権システムによって管理されるため、スコープを抜けるとスタックに格納されているBoxと、ヒープに格納されているデータの両方が解放されます。
参考: Box<T>を使ってヒープにデータを格納する

スタックとヒープについては詳しくはこちらが参考になります。
https://blog.dcs.co.jp/rust/20201217-rust-5.html

dynについてはこちら
http://doc.rust-jp.rs/rust-by-example-ja/trait/dyn.html

derive属性

derive属性とは、マクロによってトレイトの既定の振る舞いを実装させることができる機能です。例えば DebugPartialEqCopy などがあります。
例えば、構造体動詞を == 演算子で比較する場合、 PartialEq のトレイトの実装が必要です。そのためには fn eq() メソッドを実装する必要がありますが、ほとんどの場合で全フィールドが等しい場合のみ等価、そうでなければ等価ではない、という実装になります。
このようなコードを毎回書くと冗長になってしまうため、derive属性によって、マクロによりコードを生成させることができます。

User構造体にderive属性を使って、DebugとPartialEqを実装しました。コードはマクロが生成してくれるので、実装コードを書く必要はありませんが、PartialEqによって比較や、Debugによって構造体のフィールドを出力できるようになっています。

おすすめ書籍

Webアプリ開発で学ぶ Rust言語入門 パーフェクトRust プログラミングRust 第2版

blog-page_footer_336




blog-page_footer_336




-BackEnd
-

執筆者:


comment

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA


関連記事

Pythonで書かれたスクレイピングのコードをRubyで書いてみる

1 はじめに2 仕様3 ソースコード4 使用したモジュール、Gem5 対象ページを取得6 XPATHから目的のものを抜き出す7 次のページのリンクを取得する8 さいごに はじめに 以前、技術評論社さん ...

laravel logo

Laravel-debugerbarを使ってみた

1 はじめに2 インストール3 主な項目3.1 Messages3.2 Timeline3.3 Queries3.4 N+1 Queries3.5 Session3.6 Request4 さいごに5 ...

Laravelの開発環境をdocker-composeで一から構築してみる

1 はじめに2 nginxでwebサーバを立てる2.1 default.conf作成2.2 index.html作成2.3 nginxコンテナ起動3 nginxでPHP-FPMを動作させる3.1 do ...

php logo

PHP入門 〜文字列と数値の操作〜

1 はじめに2 基本ルール2.1 開始と終了のタグ2.2 ホワイトスペース2.3 大文字と小文字の区別2.4 コメント2.5 変数の定義3 文字列の操作3.1 文字列の定義3.2 無駄なスペースを削除 ...

RSpecの個人的Tips集〜その1〜

1 はじめに2 テストコードの実行をスキップする3 共通のテストコードを用意する4 外部APIの返却値をスタブにする5 さいごに はじめに みなさん、テストコードは書かれているでしょうか? 私も極力書 ...

フォロー

blog-page_side_responsive

2023年4月
 1
2345678
9101112131415
16171819202122
23242526272829
30  

アプリ情報

私たちは無料アプリもリリースしています、ぜひご覧ください。 下記のアイコンから無料でダウンロードできます。