[module] 모듈을 여러 파일로 분할

각각 자체 파일에 여러 구조체가있는 모듈을 갖고 싶습니다 . 사용하여 A Math예로서 모듈 :

Math/
  Vector.rs
  Matrix.rs
  Complex.rs

각 구조체가 동일한 모듈에 있기를 원하며 다음과 같이 기본 파일에서 사용할 것입니다.

use Math::Vector;

fn main() {
  // ...
}

그러나 Rust의 모듈 시스템 (처음에는 약간 혼란 스럽습니다)은이를 수행하는 명백한 방법을 제공하지 않습니다. 전체 모듈을 하나의 파일로만 허용하는 것 같습니다. 소박하지 않습니까? 그렇지 않은 경우 어떻게해야합니까?



답변

Rust의 모듈 시스템은 실제로 믿을 수 없을 정도로 유연하며 코드가 파일에서 어떻게 구조화되어 있는지 숨기면서 원하는 구조를 노출 할 수 있습니다.

여기서 핵심 pub use은 다른 모듈에서 식별자를 다시 내보낼 수 있는를 사용 하는 것입니다. Rust의 std::io상자에는 하위 모듈의 일부 유형 이 .NET에서 사용하기 위해 다시 내보내std::io 지는 선례가 있습니다 .

편집 (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두 개의 서로 다른 파일에 두 개의 서로 다른 모듈에 정의는 완전히 불투명하다.

와 같은 디렉토리에있는 경우 다음을 사용 main.rs하여 실행할 수 있습니다.

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

일반적으로 Rust 책 의 “Crates and Modules”장은 꽤 좋습니다. 많은 예가 있습니다.

마지막으로, Rust 컴파일러는 자동으로 하위 디렉토리를 찾습니다. 예를 들어 위의 코드는 다음 디렉토리 구조에서 변경되지 않고 작동합니다.

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

컴파일 및 실행 명령도 동일하게 유지됩니다.


답변

Rust 모듈 규칙은 다음과 같습니다.

  1. 소스 파일 자체 모듈 일뿐입니다 (특수 파일 main.rs, lib.rs 및 mod.rs 제외).
  2. 디렉토리 모듈 경로 구성 요소입니다.
  3. mod.rs 파일 은 디렉토리의 모듈 일뿐 입니다 .

math 디렉토리의 matrix.rs 1 파일 은 모듈 일뿐math::matrix 입니다. 그것은 간단합니다. 파일 시스템에서 보는 것은 소스 코드에서도 찾을 수 있습니다. 이것은 파일 경로와 모듈 경로 2 의 일대일 대응입니다 .

따라서 구조체가 math 디렉토리의 matrix.rs 파일 안에 있기 때문에을 사용 하여 구조체 Matrix를 가져올 수 있습니다 use math::matrix::Matrix. 행복하지 않습니까? 당신은 use math::Matrix;대신에 매우 선호 할 것입니다. 있을 수있다. 다음을 사용하여 math::matrix::Matrixmath / mod.rs에서 식별자 를 다시 내 보냅니다 .

pub use self::math::Matrix;

이 작업을 수행하는 또 다른 단계가 있습니다. Rust는 모듈을 로드 하기 위해 모듈 선언이 필요 합니다. mod math;main.rs에 추가하십시오. 그렇게하지 않으면 다음과 같이 가져올 때 컴파일러에서 오류 메시지가 표시됩니다.

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

여기서 힌트는 오해의 소지가 있습니다. 물론 별도의 라이브러리를 작성하려는 경우를 제외하고는 추가 상자가 필요하지 않습니다.

main.rs 상단에 다음을 추가하십시오.

mod math;
pub use math::Matrix;

모듈 선언은 하위 모듈 vector, matrix및에 대해서도 필요합니다. , 을 다시 내보내려면로드해야하기 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를 사용하는 이유입니다.

2 Java는 다릅니다. 으로 경로를 선언합니다 package. 중복됩니다. 경로는 파일 시스템의 소스 파일 위치에서 이미 분명합니다. 파일 맨 위에있는 선언에서이 정보를 반복하는 이유는 무엇입니까? 물론 파일의 파일 시스템 위치를 찾는 대신 소스 코드를 빠르게 살펴 보는 것이 더 쉽습니다. 덜 헷갈 린다고 말하는 사람들을 이해할 수 있습니다.


답변

Rusts 순수 주의자들은 아마도 저를 이단자라고 부르고이 해결책을 싫어할 것입니다. 그러나 이것은 훨씬 더 간단합니다. 각각의 파일을 각자의 파일에서 수행 한 다음 mod.rs에서 ” include! “매크로를 사용하십시오.

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

이렇게하면 중첩 된 모듈이 추가되지 않고 복잡한 내보내기 및 다시 쓰기 규칙을 피할 수 있습니다. 간단하고 효과적이며 소란스럽지 않습니다.


답변

좋아, 잠시 동안 내 컴파일러와 싸웠고 마침내 작동하게되었습니다. (을 지적한 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;

수학 / 벡터 .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로 컴파일되었습니다.


답변

여기에 Rust 파일이 깊게 중첩 될 때 어떻게 포함하는지 추가하고 싶습니다. 다음과 같은 구조가 있습니다.

|-----main.rs
|-----home/
|---------bathroom/
|-----------------sink.rs
|-----------------toilet.rs

당신은 어떻게 액세스합니까 sink.rs또는 toilet.rs에서 main.rs?

다른 사람들이 언급했듯이 Rust는 파일에 대한 지식이 없습니다. 대신 모든 것을 모듈과 하위 모듈로 간주합니다. 욕실 디렉토리에있는 파일에 액세스하려면 파일을 내보내거나 맨 위로 정렬해야합니다. 액세스하려는 디렉토리와 pub mod filename_inside_the_dir_without_rs_ext파일 내부에 파일 이름을 지정 하면됩니다.

예.

// sink.rs
pub fn run() {
    println!("Wash my hands for 20 secs!");
}

// toilet.rs
pub fn run() {
    println!("Ahhh... This is sooo relaxing.")
}
  1. 디렉토리 bathroom.rs안에 라는 파일을 만듭니다 home.

  2. 파일 이름을 내 보냅니다.

    // bathroom.rs
    pub mod sink;
    pub mod toilet;
  3. home.rsnext 라는 파일을 만듭니다.main.rs

  4. pub mod bathroom.rs 파일

    // home.rs
    pub mod bathroom;
  5. 이내에 main.rs

    // main.rs
    // Note: If you mod something, you just specify the 
    // topmost module, in this case, home. 
    mod home;
    
    fn main() {
        home::bathroom::sink::run();
    }

    use 문을 사용할 수도 있습니다.

    // main.rs
    // Note: If you mod something, you just specify the 
    // topmost module, in this case, home. 
    use home::bathroom::{sink, toilet};
    
    fn main() {
        sink::run();
        sink::toilet();
    }

하위 모듈 내에 다른 형제 모듈 (파일) 포함

경우에 당신이 사용하려는 sink.rs에서 toilet.rs당신은 지정하여 모듈을 호출 할 수 있습니다, self또는 super키워드.

// inside toilet.rs
use self::sink;
pub fn run() {
  sink::run();
  println!("Ahhh... This is sooo relaxing.")
}

최종 디렉토리 구조

다음과 같은 결과를 얻게됩니다.

|-----main.rs
|-----home.rs
|-----home/
|---------bathroom.rs
|---------bathroom/
|-----------------sink.rs
|-----------------toilet.rs

위의 구조는 Rust 2018 이상에서만 작동합니다. 다음 디렉터리 구조는 2018 년에도 유효하지만 2015 년에 사용했던 방식입니다.

|-----main.rs
|-----home/
|---------mod.rs
|---------bathroom/
|-----------------mod.rs
|-----------------sink.rs
|-----------------toilet.rs

home/mod.rs것과 동일 ./home.rs하고 home/bathroom/mod.rs동일하다 home/bathroom.rs. Rust는 디렉토리와 같은 이름의 파일을 포함하면 컴파일러가 혼란 스러울 것이기 때문에 이렇게 변경했습니다. 2018 버전 (먼저 표시된 버전)은 해당 구조를 수정합니다.

자세한 내용은 이 저장소 를 참조 하고 전체적인 설명은 이 YouTube 비디오 를 참조하십시오 .

마지막으로 … 하이픈을 피하십시오! snake_case대신 사용하십시오 .

중요 사항

당신은 반드시 깊은 파일이 최상위들에 의해 필요하지 않은 경우에도 가기 배럴 모든 파일을.

즉, sink.rs을 발견 toilet.rs하려면 위의 방법을 사용하여 main.rs!

다시 말해서, 당신이 그들을 완전히 노출하지 않는 한 do pub mod sink;또는 use self::sink; inside toilet.rs작동 하지 않습니다main.rs !

따라서 항상 파일을 맨 위에 배치하는 것을 잊지 마십시오!


답변