web-dev-qa-db-ja.com

Rustで親モジュールのインポートをどのように使用しますか?

次のようなディレクトリ構造がある場合:

src/main.rs
src/module1/blah.rs
src/module1/blah2.rs
src/utils/logging.rs

他のファイルの関数をどのように使用しますか?

Rustチュートリアルから、これを行うことができるように思えます:

main.rs

mod utils { pub mod logging; }
mod module1 { pub mod blah; }

fn main() {
    utils::logging::trace("Logging works");
    module1::blah::doit();
}

logging.rs

pub fn trace(msg: &str) {
    println!(": {}\n", msg);
}

blah.rs

mod blah2;
pub fn doit() {
    blah2::doit();
}

blah2.rs

mod utils { pub mod logging; }
pub fn doit() {
    utils::logging::trace("Blah2 invoked");
}

ただし、これによりエラーが生成されます。

error[E0583]: file not found for module `logging`
 --> src/main.rs:1:21
  |
1 | mod utils { pub mod logging; }
  |                     ^^^^^^^
  |
  = help: name the file either logging.rs or logging/mod.rs inside the directory "src/utils"

パスを下にインポートする、つまりmainからmodule1/blah.rsにインポートし、ピアをインポートする、つまりblah2からblahにインポートするように見えますが、親スコープからインポートすることはできません。

魔法の#[path]ディレクティブを使用すると、この作業を行うことができます。

blah2.rs

#[path="../utils/logging.rs"]
mod logging;

pub fn doit() {
    logging::trace("Blah2 invoked");
}

親スコープレベルから何かをインポートするには、相対ファイルパスを実際に手動で使用する必要がありますか? Rustでこれを行うより良い方法はありませんか?

Pythonでは、ローカルスコープにfrom .blah import xを使用しますが、絶対パスにアクセスする場合はfrom project.namespace.blah import xを使用できます。

34
Doug

utilsと_utils::logging_をトップレベルで宣言し、_module1::blah::blah2_内でそれらから関数を呼び出したいだけであると仮定しています。モジュールの宣言はmodで行われ、ASTに挿入され、その正規の_foo::bar::baz_スタイルのパスと、モジュールとの通常の相互作用が定義されます(宣言)はuseで行われます。

_// main.rs

mod utils {
    pub mod logging { // could be placed in utils/logging.rs
        pub fn trace(msg: &str) { println!(": {}\n", msg); }
    }
}

mod module1 {
    pub mod blah { // in module1/blah.rs
        mod blah2 { // in module1/blah2.rs

            // *** this line is the key, to bring utils into scope ***
            use utils;

            pub fn doit() {
                utils::logging::trace("Blah2 invoked");
            }            
        }
        pub fn doit() {
            blah2::doit();
        }
    }
}

fn main() {
    utils::logging::trace("Logging works");
    module1::blah::doit();
}
_

私が加えた唯一の変更は、_use utils_の_blah2_行でした。 useの仕組みの詳細については、 この回答の後半 も参照してください。 The Rust Programming Language の関連セクションも、特にこれらの2つのサブセクションの合理的なリファレンスです。

また、すべてインラインで記述し、_foo/bar.rs_の内容を_mod foo { mod bar { <contents> } }_に直接配置し、これを_mod foo { mod bar; }_に変更して、使用可能な関連ファイルを同じにする必要があります。

(ところで、println(": {}\n", msg)は2つの新しい行を出力します; _println!_はすでに1行を含んでいます(lnは "line")、print!(": {}\n", msg)またはprintln!(": {}", msg) 1つだけを印刷します。)


正確な構造を取得するには:

main.rs

_mod utils {
    pub mod logging;
}

mod module1 {
    pub mod blah;
}

fn main() {
    utils::logging::trace("Logging works");
    module1::blah::doit();
}
_

utils/logging.rs

_pub fn trace(msg: &str) { 
    println!(": {}\n", msg); 
}
_

module1/blah.rs

_mod blah2;

pub fn doit() {
    blah2::doit();
}
_

module1/blah2.rs(変更が必要な唯一のファイル)

_use utils; // this is the only change

pub fn doit() {
    utils::logging::trace("Blah2 invoked");
}
_
25
huon

これを見つけ、理解しにくい答えに完全に混乱している(私のように)他の誰かのために、私もこの質問に答えます。

それは、チュートリアルで不十分に説明されていると私が感じる2つの事柄に要約されます。

  • mod blah;構文は、コンパイラ用のファイルをインポートします。あなたはこれをコンパイルしたいすべてのファイルで使用する必要があります

  • 共有ライブラリと同様に、定義されているローカルモジュールは、use blah::blah;を使用して現在のスコープにインポートできます。

典型的な例は次のとおりです。

src/main.rs
src/one/one.rs
src/two/two.rs

この場合、useを使用して、one.rsからtwo.rsにコードを含めることができます。

use two::two;  // <-- Imports two::two into the local scope as 'two::'

pub fn bar() {
    println!("one");
    two::foo();
}

ただし、main.rsは次のようにする必要があります。

use one::one::bar;        // <-- Use one::one::bar 
mod one { pub mod one; }  // <-- Awkwardly import one.rs as a file to compile.

// Notice how we have to awkwardly import two/two.rs even though we don't
// actually use it in this file; if we don't, then the compiler will never
// load it, and one/one.rs will be unable to resolve two::two.
mod two { pub mod two; }  

fn main() {
    bar();
}

blah/mod.rsはロードとしてone/mod.rsおよびmod x;を試行するため、x.rsのようなファイルを配置することにより、x/mod.rsファイルを使用して不自然さをいくらか軽減できることに注意してください。

// one/mod.rs
pub mod one.rs

Main.rsの上部にある厄介なファイルのインポートを次のように減らすことができます。

use one::one::bar;       
mod one; // <-- Loads one/mod.rs, which loads one/one.rs.
mod two; // <-- This is still awkward since we don't two, but unavoidable.    

fn main() {
    bar();
}

Github でこれを行うサンプルプロジェクトがあります。

モジュールは、コードブロックが含まれているファイルから独立していることに注意してください。ファイルblah.rsをロードする唯一の方法はblahというモジュールを作成することですが、何らかの理由で必要な場合は、#[path]を使用してこれを回避できます。残念ながら、ワイルドカードはサポートされていないようで、複数のファイルの機能をトップレベルのモジュールに集約するのはかなり面倒です。

27
Doug

mod.rsというファイルを作成すると、rustcはモジュールをインポートするときにそれを調べます。ファイルsrc/utils/mod.rsを作成し、その内容を次のようにすることをお勧めします。

pub mod logging;

次に、main.rsで、次のようなステートメントを追加します。

use utils::logging;

そしてそれを呼び出す

logging::trace(...);

またはあなたはできる

use utils::logging::trace;

...

trace(...);

基本的に、モジュールをmod.rsファイルで宣言し、useをソースファイルで宣言します。

0
a_m0d