発端
小ネタ。お題そのままであるが、implの中身が長くなると、分けたくなる。そのためだけに別の空structを作って処理を移譲するのはなんか躊躇う。
implは分けられる
ところが、公式にもある通り、そもそもimplは複数のブロックに分けて書ける。
struct hyper_so_cool {
f1: u8,
f2: String,
}
impl hyper_so_cool {
fn f1(self) {
// do something wonderful...
}
}
impl hyper_so_cool {
fn f2(&self) {
// do something marvelous...
}
}
impl hyper_so_cool {
fn f3(&mut self) {
// do something fantastic...
}
}
ファイル分割してみる
単一ファイルの中でこの書き方をしてもあんまり意味がないので、この機能は多分ファイル分割を意識したもののはず。ほな、分けましょう。単純にモジュールに分けてみます。
mod struct_definition; // include struct hyper_so_cool
mod module1; // include func f1
mod module2; // include func f2
mod module3; // include func f3
struct hyper_so_cool {
f1: u8,
f2: String,
}
use super::hyper_so_cool;
impl hyper_so_cool {
fn f1(self) {
// do something wonderful...
}
}
use super::hyper_so_cool;
impl hyper_so_cool {
fn f2(&self) {
// do something marvelous...
}
}
use super::hyper_so_cool;
impl hyper_so_cool {
fn f3(&mut self) {
// do something fantastic...
}
}
hyper_so_cool
の定義をuse
する必要があります。そりゃそうですね。
pub
が必要になる
さて、この状態でcargo run
しようとすると、実はエラーが出ます。
なんと、hyper_so_cool
がprivate
なのでアクセスできないと言われます。
仕方がないので、公開します。
pub struct hyper_so_cool { // pub!
f1: u8,
f2: String,
}
メソッド内ではたいていの場合、それぞれのフィールドにアクセスする必要があるでしょうから(じゃなかったらメソッドにする意味はだいぶ限定されてしまう)、これだけでは不充分で、結局全部pub
をつける羽目になる。
pub struct hyper_so_cool { // pub!
pub f1: u8, // pub! pub!
pub f2: String, // pub! pub! pub!
}
すごく釈然としない。家の鍵を自分で壊した感。Rustの場合、「ファイル分割=モジュールを分ける」ということになってしまう(んですよね?)ので、こういうことになる。
サブモジュールにしたら?
でもまあ、もう一歩進んで考えてみると、後からこのstructを使いたい場合、外側からはmodule1
module2
module3
を全てuse
する必要があるので、使いにくい。普通はどうするのか、と考えると、サブモジュールである。以下のディレクトリ構成にしよう。
src
hyper_so_cool
mod.rs
module1.rs
module2.rs
module3.rs
そしてコードは以下。
mod module1;
mod module2;
mod module3;
struct hyper_so_cool { // no more pub!
f1: u8, // no more pub!
f2: String, // no more pub!
}
module1.rs
module2.rs
module3.rs
に関しては、src/hyper_so_cool
以下へ移動するだけで中身は変わらず。
というわけで、こう書くと各サブモジュールから親のstructへアクセスが可能になるみたいです。
これからはこう書きます。普通は自然とそうなるのかもしれないけれど、サブモジュールからのaccessibilityの話ってあんまり見たことがないので書いてみました。
まとめ
Rustで単一structに対するimpl
を複数に分けたい場合は、サブモジュールにして、mod.rs
にstruct定義を、それぞれのサブモジュールにimpl
を書くようにすれば、struct定義にやたらめったらpub
をつけることを防ぐことができます。
追記
@termoshttさんに、コメントで教えていただきました。ありがとうございます!
pub(crate)
を使うと、同一crate内からのアクセスだけを許すようになるみたいです。
// pub(crate) makes functions visible only within the current crate
pub(crate) fn public_function_in_crate() {
println!("called `my_mod::public_function_in_crate()");
}