web-dev-qa-db-ja.com

モジュールを複数のファイルに分割する

複数の構造体を含むモジュールが必要ですそれぞれ独自のファイルにあります例としてMathモジュールを使用:

Math/
  Vector.rs
  Matrix.rs
  Complex.rs

各構造体を同じモジュールに入れたいので、メインファイルから次のように使用します。

use Math::Vector;

fn main() {
  // ...
}

ただし、Rustのモジュールシステム(最初は少し混乱します)は、これを行うための明確な方法を提供しません。モジュール全体を1つのファイルにしか含めることができないようです。これは錆びないですか?そうでない場合、どのようにこれを行うのですか?

79
starscape

さて、しばらくコンパイラーと戦い、ついに動作するようになりました(指摘してくれたBurntSushiに感謝pub use

main.rs:

use math::Vec2;
mod math;

fn main() {
  let a = Vec2{x: 10.0, y: 10.0};
  let b = Vec2{x: 20.0, y: 20.0};
}

math/mod.rs:

pub use self::vector::Vec2;
mod vector;

math/vector.rs

use std::num::sqrt;

pub struct Vec2 {
  x: f64,
  y: f64
}

impl Vec2 {
  pub fn len(&self) -> f64 {
    sqrt(self.x * self.x + self.y * self.y) 
  }

  // other methods...
}

他の構造体も同じ方法で追加できます。注:マスターではなく、0.9でコンパイルされています。

16
starscape

Rustのモジュールシステムは実際に非常に柔軟であり、ファイル内でコードがどのように構造化されているかを隠しながら、どのような種類の構造でも公開できます。

ここでの鍵は pub use を使用することだと思います。これにより、他のモジュールから識別子を再エクスポートできます。これには、Rustのstd::ioクレートに先例があり、サブモジュールの一部のタイプは std::ioで使用するために再エクスポートされます です。

Edit(2019-08-25):答えの次の部分はかなり前に書かれました。 rustcのみでそのようなモジュール構造をセットアップする方法を説明します。今日では、ほとんどの場合、通常Cargoを使用します。以下はまだ有効ですが、その一部(#![crate_type = ...]など)は奇妙に見えるかもしれません。これは推奨される解決策ではありません。

例に合わせて、次のディレクトリ構造から始めることができます。

src/
  lib.rs
  vector.rs
main.rs

main.rsは次のとおりです。

extern crate math;

use math::vector;

fn main() {
    println!("{:?}", vector::VectorA::new());
    println!("{:?}", vector::VectorB::new());
}

そして、src/lib.rs

#[crate_id = "math"];
#[crate_type = "lib"];

pub mod vector; // exports the module defined in vector.rs

そして最後に、src/vector.rs

// exports identifiers from private sub-modules in the current
// module namespace
pub use self::vector_a::VectorA;
pub use self::vector_b::VectorB;

mod vector_b; // private sub-module defined in vector_b.rs

mod vector_a { // private sub-module defined in place
    #[derive(Debug)]
    pub struct VectorA {
        xs: Vec<i64>,
    }

    impl VectorA {
        pub fn new() -> VectorA {
            VectorA { xs: vec![] }
        }
    }
}

そして、ここで魔法が起こります。特別な種類のベクトルを実装したサブモジュールmath::vector::vector_aを定義しました。しかし、ライブラリのクライアントがvector_aサブモジュールがあることを気にしたくないのです。代わりに、math::vectorモジュールで使用できるようにします。これはpub use self::vector_a::VectorAで行われ、現在のモジュールのvector_a::VectorA識別子が再エクスポートされます。

しかし、特別なベクトル実装を異なるファイルに配置できるように、これを行う方法を尋ねました。これがmod vector_b;行の動作です。 Rustコンパイラーに、そのモジュールの実装のためにvector_b.rsファイルを探すように指示します。そして、確かに、ここにsrc/vector_b.rsファイルがあります:

#[derive(Debug)]
pub struct VectorB {
    xs: Vec<i64>,
}

impl VectorB {
    pub fn new() -> VectorB {
        VectorB { xs: vec![] }
    }
}

クライアントの観点から見ると、VectorAVectorBが2つの異なるファイルの2つの異なるモジュールで定義されているという事実は完全に不透明です。

main.rsと同じディレクトリにいる場合は、次のコマンドで実行できるはずです。

rustc src/lib.rs
rustc -L . main.rs
./main

一般的に、Rust本の "クレートとモジュール"の章 はかなり良いです。多くの例があります。

最後に、Rustコンパイラはサブディレクトリを自動的に検索します。たとえば、上記のコードはこのディレクトリ構造で変更せずに動作します。

src/
  lib.rs
  vector/
      mod.rs
      vector_b.rs
main.rs

コンパイルおよび実行するコマンドも同じままです。

97
BurntSushi5

Rustモジュールのルールは次のとおりです。

  1. ソースファイル単なる独自のモジュール(特別なファイルmain.rs、lib.rs、mod.rsを除く)。
  2. ディレクトリ単なるモジュールパスコンポーネント。
  3. ファイルmod.rs ちょうどディレクトリのモジュール。

ファイルmatrix.rs1 ディレクトリmath is justモジュールmath::matrixで。それは簡単です。あなたがあなたのソースコードで見つけるあなたのファイルシステムで見るもの。これは、ファイルパスとモジュールパスの1対1の対応です2

構造体はディレクトリmathのファイルmatrix.rs内にあるため、構造体Matrixuse math::matrix::Matrixでインポートできます。満足していない?代わりにuse math::Matrix;を非常に好むでしょう。それが可能だ。次を使用して、math/mod.rsの識別子math::matrix::Matrixを再エクスポートします。

pub use self::math::Matrix;

これを機能させるには別のステップがあります。 Rustはloadモジュールへのモジュール宣言が必要です。main.rsにmod math;を追加します。そうしないとエラーが発生します。このようにインポートするときのコンパイラからのメッセージ:

error: unresolved import `math::Matrix`. Maybe a missing `extern crate math`?

ここでヒントが誤解を招く可能性があります。もちろん、別のライブラリを作成するつもりである場合を除き、追加のクレートは必要ありません。

これをmain.rsの上部に追加します。

mod math;
pub use math::Matrix;

モジュールの宣言は、サブモジュールvectormatrix、およびcomplexにも必要です。これは、mathを再エクスポートするためにロードする必要があるためです。識別子の再エクスポートは、識別子のモジュールをロードした場合にのみ機能します。つまり、math::matrix::Matrixという識別子を再エクスポートするには、mod matrix;と記述する必要があります。これは、math/mod.rsで実行できます。したがって、このコンテンツでファイルを作成します。

mod vector;
pub use self::vector::Vector;

mod matrix;
pub use self::matrix::Matrix;

mod complex;
pub use self::complex::Complex;

ああ、これで完了です。


1ソースファイル名は通常、Rustでは小文字で始まります。そのため、Matrix.rsではなくmatrix.rsを使用します。

2Javaは異なります。パスもpackageで宣言します。冗長です。パスは、ファイルシステム内のソースファイルの場所からすでに明らかです。ファイルの先頭の宣言でこの情報を繰り返すのはなぜですか?もちろん、ファイルのファイルシステムの場所を見つけるのではなく、ソースコードを簡単に確認する方が簡単な場合があります。分かりやすいと言う人を理解できます。

27
nalply

Rustsの純粋主義者はおそらく私を異端者と呼んでこの解決策を嫌いますが、これははるかに簡単です:各ファイルを独自のファイルで実行し、mod.rsで「 include! 」マクロを使用してください:

include!("math/Matrix.rs");
include!("math/Vector.rs");
include!("math/Complex.rs");

そうすれば、ネストされたモジュールを追加する必要がなくなり、複雑なエクスポートおよび書き換えルールを回避できます。シンプル、効果的、大騒ぎ。

14
hasvn