「関数」「構造体」「トレイト」「マクロ」について
最後に関数、構造体、トレイト、マクロについて解説します。今回の解説でRustのすべてとは言えませんが、今回の文法が分かれば簡単なことならある程度のコードが書けると思います。
関数はプログラムが最初に実行される「main」関数がありましたね。関数が呼ばれると、その後に続く「{}」で括ったコードが実行されます。構造体は「class(クラス)」みたいなもので、共通する構造体をトレイトで一度に定義できます。C++では親クラスを派生することでトレイトに似た機能を実装していました。
マクロは簡単に言えばC言語の「#define」です。C言語でも#defineをマクロと呼んでいましたね。
「関数」について
関数は、何行かのプログラムをまとめて同じような処理を何度も書かなくても済むようにします(図8)。一般のプログラミング言語と同様に、関数には「関数名」「引数」「戻り値」があります。関数名は必須ですが、引数と戻り値は任意です。ただし、関数は「fn」で宣言します。
図8:「関数」の説明図
・「関数」のサンプルコード
2 | let s = get_str("文字取得"); |
6 | fn get_str(s:&str) -> String { |
【サンプルコードの解説】
「get_str」関数に"文字取得"引数を渡して"文字取得の関数です。"を戻り値として「s」変数に受け取って代入し「let」で宣言します。その文字列をターミナルに表示します。
このサンプルコードでは「get_str」関数を「main」関数の後に書いていますが、コーディングの順番は関係ありません。「get_str」関数を「main」関数の前に書いても同じことです。
「構造体」について
構造体は、同じグループに1つ以上(通例は複数)の機能をまとめて表現します(図9)。一般のプログラミング言語はclassでオブジェクト指向を表すことが多いですが、Rustではstruct(構造体)でオブジェクト指向を表します。オブジェクト指向を使えば、例えばロボットの設計図を構造体で作り、そのインスタンス(実体)を生成すればリアルのロボットが作れるのに似ています。
structでは、「プロパティ(構造体に所属する変数)」を定義します。さらに「impl」で「メソッド(構造体に所属する関数)」を定義します。
図9:「構造体」の説明図
・「構造体」のサンプルコード
07 | fn new(n:u32,p:u32) -> Self { |
13 | fn sum(&self) -> u32 { |
19 | let drink = Drink::new(2,100); |
20 | println!("ドリンクの合計金額は{}円",drink.sum()); |
【サンプルコードの解説】
「struct Drink」で「num」プロパティと「price」プロパティを持つ「Drink」構造体を宣言します。「impl Drink」で「Drink」構造体の「コンストラクタ」の「new」メソッドと、「sum」メソッドを宣言します。
「let drink = Drink::new(2,100);」で「Drink」構造体に2と100を「引数」に渡したインスタンスを「drink」変数に代入し「let」で宣言します。
「println!」マクロで「Drink」構造体の「sum」メソッドを呼び出し、ドリンクの個数×単価の合計金額200円を取得して"ドリンクの合計金額は200円"をターミナルに表示します。
「トレイト」について
トレイトは、同じような構造体を一度に定義できます(図10)。一般のプログラミング言語でいう「interface(インターフェース)」に似ていると言えばピンとくる方もいるでしょう。実は筆者はあまりinterfaceを使ってこなかったのですが、トレイトも重要な文法です。
トレイトで共通するメソッドを定義し、トレイトから構造体ごとに派生させて共通するメソッドを実装します。
図10:「トレイト」の説明図
・「トレイト」のサンプルコード
02 | fn price(&self) -> u32; |
03 | fn name(&self) -> &str { return "Drink" } |
06 | struct Coffee { num: u32, unit_price: u32 } |
08 | impl Drink for Coffee { |
09 | fn price(&self) -> u32 { |
10 | self.num * self.unit_price |
12 | fn name(&self) -> &str { return "Coffee" } |
15 | struct Orange { num: f32, unit_price: f32, discount: f32 } |
17 | impl Drink for Orange { |
18 | fn price(&self) -> u32 { |
19 | (self.num * self.unit_price * self.discount) as u32 |
21 | fn name(&self) -> &str { return "Orange" } |
25 | let a = Coffee { num: 1, unit_price: 200 }; |
26 | let b = Orange { num: 3_f32, unit_price: 100.0, discount: 0.9 }; |
27 | println!("{} price={}", a.name(), a.price()); |
28 | println!("{} price={}", b.name(), b.price()); |
【サンプルコードの解説】
「pub trait Drink」で「price」メソッドと「name」メソッドを持つ「Drink」トレイトを宣言します。
「struct Coffee」で「num」プロパティと「unit_price」プロパティを持つ「Coffee」構造体を宣言します。「impl Drink for Coffee」で「Drink」トレイトを埋め込んだ「Coffee」構造体を実装します。
「struct Orange」で「num」プロパティと「unit_price」プロパティと「discount」プロパティを持つ「Orange」構造体を宣言します。「impl Drink for Orange」で「Drink」トレイトを埋め込んだ「Orange」構造体を実装します。
「Coffee」構造体からインスタンスを生成し「a」変数に代入します。「Orange」構造体からインスタンスを生成し「b」変数に代入します。
最後にコーヒーとオレンジジュースの名前と値段をターミナルに表示します。
「マクロ」について
マクロは、これまでにも出てきた「println!」のように「!」で名前が終わる関数のようなものです。マクロを元に他のコードを記述するコードを書く手段であり、つまりメタプログラミングです。また、マクロには他にも「宣言的(declarative)マクロ」があります。例えば「vec!」マクロのように配列を可変長ベクトルにできるものなどです。
マクロはビルド時にコードが書き換えられて、実行プログラムでは既に変換されたプログラムになっています。そのため実行時に変換されるより高速に処理ができます。
おわりに
今回は、Rustの最小限の基本的な文法について解説しました。次回で解説する「所有権」を除けば、何となくC++やJavaに近かったのではないでしょうか。