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


関連記事

Go言語

GoのWeb Application Framework

1 はじめに2 代表的なGoのWAF2.1 軽量なWAF2.2 フルスタックなWAF3 Ginを使ってみる3.1 クエリパラメータ+ポストパラメータ3.2 ファイルアップロード3.3 URLのグループ ...

Go言語

Go 1.18のWorkspacesモードでマルチモジュール化

1 はじめに2 マルチモジュール構成2.1 非Workspacesモードの場合2.2 Workspacesモードの場合3 go workコマンド3.1 init3.2 edit3.3 sync3.4 ...

laravel logo

LaravelでHTTP通信

1 はじめに2 インストール3 基本的な使い方3.1 GETでリクエストを投げる3.2 リクエストヘッダーを指定3.3 クエリパラメータを指定3.4 timeout時間を指定3.5 POSTでリクエス ...

laravel logo

Deployerで複数の環境やサーバにデプロイするには

1 はじめに2 hosts.ymlファイルの作成3 プロダクション・ステージング環境を分ける3.1 ymlファイルの記載3.2 デプロイコマンド4 複数台のサーバに同時リリースする4.1 ymlファイ ...

Go言語

Goのfmt.print系関数のまとめ

1 はじめに2 print関数の命名規則3 各print関数の説明3.1 Print(標準出力へ出力)系関数3.2 Sprint(文字列を出力)系関数3.3 Fprint(ファイルへ出力)系関数4 書 ...

フォロー

blog-page_side_responsive

2023年4月
 1
2345678
9101112131415
16171819202122
23242526272829
30  

アプリ情報

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