Rustで自作モジュールを読み込む歩法を学ぶ
プログラミングの基本構文はもう散々やってきたので覚えなくても必要なときに覚えたらいいのですが、 Rustはいろんな意味で触るのが始めてなので久々に基本構文を交えて勉強していこうかなと思います。
とりあえずHello Worldをやって終わってしまう人も意外と多い(Rustの場合私がそう) ので、その次へ進むためにやっていきましょうと行った感じです。
ただ、普通に基本構文をやってもなにもつまらないので せっかくなのでなにか便利なライブラリかフレームワーク的なのを導入して動かせるようにしてみましょう。
自作のライブラリを読み込んで見る
Rustのライブラリ、モジュールの読み込みはちょっと特殊で Node.jsとかC++とかだとパスを指定してインクルードしたりインポートしたりします。
Rustはモジュールを読み込む時、パスの指定という概念がないので モジュール名を指定するだけになります。
基本的には同じ階層のものしか指定できません。
まずはmain.rsと同じ階層のモジュールを作る
ともかくモジュールの読み込みを体験してみましょう。
// mylibs.rs
pub mod hogeVal {
pub fn getVal() -> i32 {
return 1234;
}
}
pub mod piyoStr {
pub fn getStr() -> String {
return String::from("String CharSet");
}
}
main.rsと同じ階層にmylibs.rsと名付けて上記のコードを保存します。
Rustの文字列型にちょっと悩みましたが、調べるとどうやらプリミティブ型のstr型と 標準ライブラリのString型があるそうで、厳密には全然違うのですが str型は静的配列みたいな感じでString型は動的配列みたいな扱いになるようです。
ただ、単純に文字列の連携とかは出来ないのでちょっと変わったやり方をしないといけなかったり 文字列周りは結構特殊な感じがしたので、ある程度慣れたらそのうち文字列について記事を書こうかなと思います。
話を戻しますと「hogeVal」「piyoStr」というモジュールを定義しました。
モジュールは「mod」をつけて宣言します。
pubはpublicの略ですね。
付けないとおそらくデフォルトでprivate状態になって外部から使用できない気がするので とりあえずテストですし、わからない内は基本pubを付けておきましょう。
次にmain.rsでこのmylibs.rsで作ったモジュールを使えるようにします。
// main.rs
mod mylibs;
fn main() {
println!("{}", mylibs::hogeVal::getVal().to_string());
println!("{}", mylibs::piyoStr::getStr());
}
「mod ファイル名」で読み込むことが出来ます。
そしてその名前にコロンを2つ付けてつないでアクセスする感じになります。
jsとかはドットでチェーンしていきますが、この辺はC++と似たような感じですね。
それにしても長ったらしいですね……
println!にもそのまま引数に関数を渡すだけじゃエラーがでるので、 第一引数に「"{}"」を渡す必要がありました。
この辺はなれるしかなさそうです。
モジュールを作るときは階層を作って整理したい
最初にも言ったとおりモジュール読み込みはディレクトリパスを指定するということができないので、 普通のやり方ではmain.rsと同じ階層のものしか読み込めません。
となると開発を進めていくとsrcディレクトリ直下がぐちゃぐちゃになってしまうのが容易に想像できますね。
そこで解決策として1つディレクトリを作ってその中に「mod.rs」というファイルを作れば そのディレクトリはモジュールとして認識するようになります。
mod.rsの中身は一旦空にしておいてください。
mod.rsというファイルそのものが存在していることがフラグとなっているみたいです。
とりあえず先程作ったmylibsをディレクトリを切ってみましょう。
src直下に「libs」というディレクトリを作成してmylibs.rsを移動させてください。
libsディレクトリにmod.rsファイルを作成するのを忘れないようにしましょう。
おそらくどうやって呼び出すかは大体検討がつくと思うので以下のコードを見てください。
// main.rs
mod libs;
fn main() {
println!("{}", libs::mylibs::hogeVal::getVal().to_string());
println!("{}", libs::mylibs::piyoStr::getStr());
}
modで先ほどのlibsディレクトリ名を指定します。
ですがここで「mylibs」がエラーになってしまいます。
予測変換でそれ以下の関数もでてくるのにおかしいですね。
問題はlibsはモジュール化はしましたが、libsがmylibsを呼び出していないのが問題になっています。
ということで先程作ったmod.rsにモジュールとして自分の階層にある「mylibs」を呼び出してみましょう。
// libs/mod.rs
pub mod mylibs;
これだけです。
modの前にpubを付け忘れずに。
付けないとprivate扱いになります(1敗)
これでmain.rs側はエラーが消えました。
そしてこのmodファイルのやり方はどうやら古いやり方だそうなのですが 互換があるようで一応使えるようです。
今から始める人は次の新しいやり方を覚えておいたほうがよさそうです。
2018年以降のモジュール作成方法
mod.rsでのやり方は2015年に実装されたやりかたで、 次にやるのは2018年以降のやりかただそうです。
先程も言ったように互換があるので古いやり方もそのまま使えます。
ディレクトリと同じ名前のファイルを作る
2018年の方法とは、モジュールとしたいもののファイルとディレクトリを作るということです。
同名のファイルとディレクトリがアレばmod.rsの必要がなくなるというわけですね。
例えばmvc的な構造のものを作ると以下の様なディレクトリ構成になります。
// src/
// src/main.rs
// src/models.rs
// src/models/
// src/controllers.rs
// src/controllers/
// src/views.rs
// src/views/
うーん、これならmod.rs使ったほうが見やすいような気がするんですがどうなんでしょうね……
とりあえず話を戻して2018年のやり方でlibsモジュールを読み込めるようにしてみましょう。
ここまで沿ってやっているのであれば、main.rsと同じ階層にlibsディレクトリはもう存在しているので libsディレクトリにあるmod.rsをリネームしてlibs.rsにしてsrcディレクトリに移し替えましょう。
これでmod.rsがなくてもlibs.rsとlibsディレクトリが同じ階層になりました。
試しにlibs.rsの中身のコードをコメントアウトするとmain.rsでモジュール読み込みエラーが出るのがわかります。
これで基本的な自作モジュールの読み込み方法は理解できたと思います。
階層を深くしたい場合は同じようにlibsディレクトリの中に同じファイル名とディレクトリを作って その中にモジュールを定義していけばいけます。
ただ、このままだと階層が深くなるにつれてチェーンしていく文字列が増えていくので、 可読性も下がりますしとてつもなくブサイクなコードになります。
モジュールの呼び出し型を簡略化するエイリアス
モジュールの作り方と呼び出し方がわかったところで、 現状ではコードが長くなってしまうのでその対策としてエイリアスを登録できます。
現状1つの階層でも「libs::mylibs::piyoStr::getStr()」とめちゃくちゃ長いですね。
せめて「piyoStr::getStr()」までにしておきたいです。
こんなときは「use~as」を使うとエイリアスが作成されます。
mod libs;
use libs::mylibs::hogeVal as GETVAL;
use libs::mylibs::piyoStr as GETSTR;
fn main() {
println!("{}", GETVAL::getVal().to_string());
println!("{}", GETSTR::getStr());
}
こんな感じに省略可できるようになります。
asの後ろの名前になるものは、わかりやすいように現状すべて大文字にしてますが任意の名前で大丈夫です。
エイリアス名をつけなくてもいける
mod libs;
use libs::mylibs::hogeVal;
use libs::mylibs::piyoStr;
fn main() {
println!("{}", hogeVal::getVal().to_string());
println!("{}", piyoStr::getStr());
}
このようにasを付けて名前をつけなくてもモジュール名そのままでもいけるみたいです。
モジュール名をつけるときにしっかりしておけばエイリアスの名前を付ける必要はなさそうですね。
それにしてもなれるまで時間がかかりそうですねこれは……
次回はすでに作られているライブラリを読み込んでみようと思います。
Rustはデフォルトでcargoというパッケージマネージャーがあるので、 おそらくnode.jsでいう「npm install modulename」みたいな感じで モジュールを管理できるんじゃないかなと予想しています。
現時点ではまだ調べてないので次回の記事で明らかにしてみます。
それでは。