経緯
それなりにコードサイズが膨らんできたので、「RUSTでファイル分割」なんかを見て、ちまちまファイル分割をした。ロガーをラップするモジュール、メール通知を送信するモジュール、社内基準を満たす暗号化を施すモジュール、ファイルサーバに保存するモジュール、ディレクトリ走査をして必要なファイルを特定したりするモジュール、WEBアプリのAPIをたたくモジュール、HTMLを生成してデプロイするモジュール、などなど。まあこういう当たり前のモジュールがひととおりそろってくると、「やりたいことがサクッとできるひと」になれるわけだ。入門期の辛抱ですな。モジュールに分けるのは、「hogehoge.rs」を作ってコードをコピーして、「main.rs」に「mod hogehoge; use hogehoge;」を足すだけの簡単なお仕事だが、もうすでにご想像のように、「ファイルサーバに保存するモジュール」は「社内基準を満たす暗号化を施すモジュール」の関数を呼び出す必要がある。そこで詰まった。やり方があんまり書かれていないような気がするのだ。少なくとも、私は小一時間ハマった。なので書く。
シンプルな例で考えてみる
下記のように、コードを書いたとする。あほみたいなprintln!の劣化ラッパーだ。
fn main() {
printstring("Hello, world!");
moduser("hoge ");
}
pub fn moduser(text: &str) {
printstring((text.to_string()+"fuga").as_str());
}
pub fn printstring(text: &str) {
println!("{}", text);
}
コードがでかくなってきたので、printstringをprintモジュールにする。こんな感じだ。
mod print;
fn main() {
print::printstring("Hello, world!");
moduser("hoge ");
}
pub fn moduser(text: &str) {
print::printstring((text.to_string()+"fuga").as_str());
}
pub fn printstring(text: &str) {
println!("{}", text);
}
ここまではいい。とても楽ちんだ。しかし、moduser関数、つまり、すでに単一ファイルのモジュールとして切り出した関数を呼び出す関数を外だしすると、途端に悩ましくなる。
ダメな例
まずは、ダメな例。
mod print;
mod third;
fn main() {
print::printstring("Hello, world!");
third::moduser("hoge ");
}
pub fn printstring(text: &str) {
println!("{}", text);
}
mod print;//→ダメ!
pub fn moduser(text: &str) {
print::printstring((text.to_string()+"fuga").as_str());
}
こうすると、third.rsのコメントを入れた行で怒られる。third.rsからは、mod文でprint.rsが見えないみたいなのだ。ちゃんとわかっていないで書くけれども、main.rs, mod.rs, lib.rsと、それ以外のファイルでは、見えるモジュールの範囲が違うということなのかもしれない。その辺は別にコンパイラを読む体力はないので、詳しい人に補足してもらえると嬉しい。
怒られない例
正解はこれ。super::を使う。これって、知らないと何ともならんので、コードリーディング不足の初学者は必ずハマるんじゃないかと思うんだけど、世の中に説明がほとんどない。そりゃそうかもしれないけど、でも、初心者ってそういうもんだよね。
pub fn moduser(text: &str) {
//このようにsuper::経由でアクセスしないといけない
super::print::printstring((text.to_string()+"fuga").as_str());
}
でも、このやり方だと、がっつりコードを書き換える必要が出ちゃうので、それは問題だよね。
だからこうする。
use super::print;
pub fn moduser(text: &str) {
print::printstring((text.to_string()+"fuga").as_str());
}
こうするのが正解でした。何てことはないし、多分数週間後には自分もそれをあえて説明しようとは思わなくなってるような気がするので、今のうちに書いておく。