LoginSignup
10
8

More than 5 years have passed since last update.

Rustでimplの実装を複数ファイルに書く

Last updated at Posted at 2017-11-28

発端

小ネタ。お題そのままであるが、implの中身が長くなると、分けたくなる。そのためだけに別の空structを作って処理を移譲するのはなんか躊躇う。

implは分けられる

ところが、公式にもある通り、そもそもimplは複数のブロックに分けて書ける。

src/lib.rs
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...
  }
}

ファイル分割してみる

単一ファイルの中でこの書き方をしてもあんまり意味がないので、この機能は多分ファイル分割を意識したもののはず。ほな、分けましょう。単純にモジュールに分けてみます。

lib.rs
mod struct_definition; // include struct hyper_so_cool
mod module1; // include func f1
mod module2; // include func f2
mod module3; // include func f3
src/struct_definition.rs
struct hyper_so_cool {
  f1: u8,
  f2: String,
}
src/module1.rs
use super::hyper_so_cool;

impl hyper_so_cool {
  fn f1(self) {
    // do something wonderful...
  }
}
src/module2.rs
use super::hyper_so_cool;

impl hyper_so_cool {
  fn f2(&self) {
    // do something marvelous...
  }
}
src/module3.rs
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_coolprivateなのでアクセスできないと言われます。
仕方がないので、公開します。

src/struct_definition.rs
pub struct hyper_so_cool { // pub!
  f1: u8,
  f2: String,
}

メソッド内ではたいていの場合、それぞれのフィールドにアクセスする必要があるでしょうから(じゃなかったらメソッドにする意味はだいぶ限定されてしまう)、これだけでは不充分で、結局全部pubをつける羽目になる。

src/struct_definition.rs
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

そしてコードは以下。

src/hyper_so_cool/mod.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()");
    }
10
8
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
8