モジュールとは
アプリケーション内のいくつかのコードを、まとまりで分けて管理するための仕組みです。モジュール同士は独立しており、お互いに隠蔽されています。モジュールには関数や構造体、他のモジュールなどを含めることができます。
モジュールの宣言
mod
キーワードを使うとモジュールを宣言できます。{}
ブロックに書かれたものがそのモジュールに属します。
mod my_module {
pub fn greet() {
println!("Hello from my_module!");
}
fn hidden_function() {
println!("This is private.");
}
}
モジュール内の要素の可視性
モジュール内の要素はデフォルトでプライベートです。すなわち、他のモジュールからは参照したり呼び出したりすることができません。
他のモジュールからもアクセス可能にするためにはpub
キーワードを用います。pub(オプション)
の形で、細かくアクセスを制御することができます。
mod my_module {
pub fn public_function() {
println!("This is a public function.");
}
fn private_function() {
println!("This is a private function.");
}
pub(crate) fn crate_function() {
println!("This function is visible within the crate.");
}
}
fn main() {
my_module::public_function();
// my_module::private_function(); // コンパイルエラー: `private_function` is private
my_module::crate_function();
}
可視性はあまり意識したことがないので、このあたりのユースケースを理解しきれていません。どちらかというとJavaを思い出しました。
モジュールのネスト
モジュールの中にさらに別のモジュールを含めることができます。
mod parent_module {
pub mod child_module {
pub fn say_hello() {
println!("Hello from the child module!");
}
}
fn private_parent_function() {
println!("This is private to the parent module.");
}
}
fn main() {
parent_module::child_module::say_hello();
// parent_module::private_parent_function(); // コンパイルエラー
}
モジュールの参照
すでにサンプルコードで示している通り、他のモジュールの要素にアクセスするときは、モジュール名::要素名
の形でアクセスします(ネストしている場合はさらに続きます)。
毎回絶対パスを書くのが面倒な場合は、use
キーワードでプレフィックス部分を省略できます。モジュール間で同名の要素があって名前が衝突する場合、短い名前で参照したい場合などでは、as
キーワードで別名をつけることができます。
mod math {
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
pub fn subtract(a: i32, b: i32) -> i32 {
a - b
}
}
use math::add;
use math::subtract as sub;
fn main() {
let sum = add(3, 4);
let difference = sub(10, 3);
println!("Sum: {}", sum);
println!("Difference: {}", difference);
}
このあたりはTypeScriptの名前付きエクスポートやimport
周りに近しいものを感じます。
個人的には面倒でなければできる限り絶対パスで書きたくなる派です。
構造体のフィールドの可視性
あるモジュールの構造体のフィールドを他のモジュールから参照する場合、その構造体がパブリックであることに加え、参照したいフィールドもパブリックである必要があります。
プライベートなフィールドがある場合は直接構造体のインスタンスを生成できません(下記でいうとPublicStruct { ... }
と書けない)。ただし、インスタンスを返却するコンストラクタをパブリックで宣言しておけば、それを使うことでインスタンスを生成できます。
mod my_module {
pub struct PublicStruct {
pub public_field: String,
private_field: String,
}
impl PublicStruct {
pub fn new(public: &str, private: &str) -> Self {
PublicStruct {
public_field: public.to_string(),
private_field: private.to_string(),
}
}
}
}
fn main() {
let instance = my_module::PublicStruct::new("Hello", "Hidden");
println!("Public field: {}", instance.public_field);
// println!("Private field: {}", instance.private_field); // コンパイルエラー
}
ファイル構造との関係性
モジュールは別ファイルに分けることが一般的と思われます。そのファイルの中でモジュールとそうでない部分が混在するのであればmod モジュール名 {...}
の形を取りますが、ファイル全体が一つのモジュールなのであれば、ファイル名がそのままモジュール名となります。
モジュールを参照する側は、mod モジュール名;
でどのモジュールを参照するかを明記します。
src/
├── main.rs
├── my_module.rs
└── my_module/
└── child_module.rs
pub fn greet() {
println!("Hello from my_module!");
}
mod my_module;
fn main() {
my_module::greet();
}
モジュールがネストしている場合は、親モジュールのファイル名と同じディレクトリを作成し、その下に子モジュールを作成します。このときも、子モジュールのファイル名がそのまま子モジュール名になります。
pub fn child_function() {
println!("Hello from the child module!");
}
pub mod child_module;
pub fn parent_function() {
println!("Hello from the parent module!");
}