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


関連記事

Stripe Connectを使ってCheckoutを利用した継続課金を実装

1 はじめに1.1 Checkoutを使う場合の動線2 決済画面への遷移2.1 マイグレーション2.2 Checkout Sessionの作成3 決済完了後の制御4 おまけ4.1 ローカルでWebho ...

Go言語

goroutineとchannelとContext

1 はじめに2 並行処理と並列処理3 goroutine4 channel4.1 channelからデータ受信4.2 for-range でのデータ受信4.3 複数のchannelを受信4.4 buf ...

軽量なAlpine Linuxイメージでgitbookのローカル環境を構築する

1 はじめに2 Alpine Linuxとは3 Docker本体のインストール4 サンプルリポジトリのダウンロード5 dockerイメージ作成6 Gitbook初期化&実行7 Dockerの ...

laravel logo

Laravel Admin でCSVインポートを実装する

1 はじめに2 下準備2.1 Laravel Adminの導入2.2 Laravel Excelの導入3 Laravel Excelの実装3.1 Importクラスの作成3.2 モデルのfillabl ...

laravel logo

Laravel SailでDocker環境構築

1 はじめに2 Laravel Sailの基本2.1 Dockerの構成2.2 コンテナの起動・停止2.3 sailで使用できるコマンド3 Laravel Sailの設定3.1 ポートフォワードの設定 ...

フォロー

blog-page_side_responsive

2023年4月
 1
2345678
9101112131415
16171819202122
23242526272829
30  

アプリ情報

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