Rust中的代碼組織:package/crate/mod


剛接觸Rust遇到一堆新概念,特別是package, crate, mod 這些,特別迷糊,記錄一下

一、pakcage與crate

當我們用cargo 創建一個新項目時,默認就創建了一個package,參考下面的截圖:

這樣就生成了一個名為demo_1的package,另外也創建1個所謂的binary crate,當然也可以加參數 --lib生成library的crate

然后在crate里,又可以創建一堆所謂的mod(模塊),因此整體的關系,大致象下面這張圖:

即:

  • 1個Package里,至少要有1種Crate(要么是Library Crate,要么是Binary Crate)
  • 1個Package里,最多只能有1個Library Crate
  • 1個Package里,可以有0或多個Binary Crate
  • 1個Crate里,可以創建0或多個mod(后面還會詳細講mod)

 

二、crate的入口

通常在創建項目后,會默認生成src/main.rs,里面有1個main方法:

(base) ➜  code tree demo_1
demo_1
├── Cargo.toml
└── src
    └── main.rs

main.rs的內容:

fn main() {
    println!("Hello, world!");
}

這個就是crate運行時的入口函數,前面我們提過,1個package里,允許有1個library crate和多個binary crate,我們弄個復雜點的場景:

(base) ➜  demo_1 git:(master) ✗ tree
.
├── Cargo.lock
├── Cargo.toml
└── src
    ├── lib.rs
    ├── main.rs
    └── main2.rs

在src里,再加2個文件lib.rs及main2.rs,內容如下:

lib.rs

pub fn foo(){
    println!("foo in lib");
}

main2.rs

fn main(){
    demo_1::foo();
    println!("hello 2");
}

同時把main.rs里也加一行demo_1::foo(),讓它調用lib.rs里的foo()方法

fn main() { 
   demo_1::foo(); 
   println!("Hello, world!"); 
}

看上去,我們有2個main入口函數了,運行一下看看結果如何:

(base) ➜  demo_1 git:(master) ✗ cargo run
   Compiling demo_1 v0.1.0 (/Users/jimmy/code/demo_1)
    Finished dev [unoptimized + debuginfo] target(s) in 0.70s
     Running `target/debug/demo_1`
foo in lib
Hello, world!

從最后2行的輸出來看,運行的是main.rs中的方法,即:main2.rs中的main函數,並未識別成入口,繼續折騰,在src下創建目錄bin,然后把main.rs以及main2.rs都移動到bin目錄

(base) ➜  demo_1 git:(master) ✗ tree
.
├── Cargo.lock
├── Cargo.toml
└── src
    ├── bin
    │   ├── main.rs
    │   └── main2.rs
    └── lib.rs

然后再運行: 

(base) ➜  demo_1 git:(master) ✗ cargo run  
error: `cargo run` could not determine which binary to run. Use the `--bin` option to specify a binary, or the `default-run` manifest key.
available binaries: main, main2

這次提示不一樣了,大意是說有2個入口main, main2,不知道該運行哪一個,需要加參數明確告訴cargo,加1個參數 --bin main2

(base) ➜  demo_1 git:(master) ✗ cargo run --bin main2
   Compiling demo_1 v0.1.0 (/Users/jimmy/code/demo_1)
    Finished dev [unoptimized + debuginfo] target(s) in 0.62s
     Running `target/debug/main2`
foo in lib
hello 2

這樣就可以了

 

三、 mod

3.1 定義mod

把main.rs里加點代碼:

mod a {
    pub fn foo_a_1() {
        println!("foo_a_1");
    }

    fn foo_a_2(){
        println!("foo_a_2");
    }

    mod b {
        pub fn foo_b() {
            foo_a_2();
            println!("foo_b");
        }
    }
}

fn main() {
    a::foo_a_1();
    a::foo_a_2();
    a::b::foo_b();
}

解釋一下:

  • 用mod關鍵字,定義了模塊a,然后里面還嵌套了模塊b
  • 然后在main方法里,嘗試調用a模塊的方法,以及其子模塊b中的方法

編譯一下,會發現各種報錯:

-----------------------------------------------------

(base) ➜ demo_1 git:(master) ✗ cargo build
Compiling demo_1 v0.1.0 (/Users/jimmy/code/demo_1)
error[E0425]: cannot find function `foo_a_2` in this scope
--> src/bin/main.rs:12:13
|
12 | foo_a_2();
| ^^^^^^^ not found in this scope
|
help: consider importing this function
|
11 | use crate::a::foo_a_2;
|

error[E0425]: cannot find function `foo_a` in module `a`
--> src/bin/main.rs:19:8
|
19 | a::foo_a();
| ^^^^^ not found in `a`

error[E0603]: module `b` is private
--> src/bin/main.rs:20:8
|
20 | a::b::foo_b();
| ^ private module
|
note: the module `b` is defined here
--> src/bin/main.rs:10:5
|
10 | mod b {
| ^^^^^

Some errors have detailed explanations: E0425, E0603.
For more information about an error, try `rustc --explain E0425`.
error: could not compile `demo_1` due to 3 previous errors

-----------------------------------------------------

從提示上看,主要是private的問題:

  • 默認情況下Rust里的函數以及模塊,都是private作用域的,外界無法訪問,所以要改成pub

修改一下:

mod a {
    pub fn foo_a_1() {
        println!("foo_a_1");
    }

    //修改1:加pub
    pub fn foo_a_2(){
        println!("foo_a_2");
    }

    //修改2:加pub
    pub mod b {
        pub fn foo_b() {
            //修改3:調用父mod的方法,要加super關鍵字
            super::foo_a_2();
            println!("foo_b");
        }
    }
}

fn main() {
    a::foo_a_1();
    a::foo_a_2();
    a::b::foo_b();
}

再運行:

(base) ➜  demo_1 git:(master) ✗ cargo run
   Compiling demo_1 v0.1.0 (/Users/jimmy/code/demo_1)
    Finished dev [unoptimized + debuginfo] target(s) in 0.33s
     Running `target/debug/main`
foo_a_1
foo_a_2
foo_a_2
foo_b

正常了,但是這里可能有同學會疑問:mod a不也沒加pub關鍵字嗎,為啥main能正常調用?可以先記一條規則 :如果模塊x與main方法在一個.rs文件中,且x處於最外層,main方法可以調用x中的方法

再微調下代碼:

mod a {
    //修改:去掉pub
    fn foo_a_2(){
        println!("foo_a_2");
    }

    pub mod b {
        pub fn foo_b() {
            super::foo_a_2();
        }
    }
}

fn main() {
    a::b::foo_b();
}

再次運行:

(base) ➜  demo_1 git:(master) ✗ cargo run
   Compiling demo_1 v0.1.0 (/Users/jimmy/code/demo_1)
    Finished dev [unoptimized + debuginfo] target(s) in 0.42s
     Running `target/debug/main`
foo_a_2

疑問:父模塊mod a中的foo_a_2沒加pub,也就是默認private,為啥子模塊b能調用?又是一條規則 :子模塊可以調用父模塊中的private函數,但是反過來是不行的 (通俗點講:老爸的錢,就是兒子的錢,但是兒子的錢,除非兒子主動給老爸,否則還是兒子的!想必Rust的設計者們,深知“父愛如山”的道理)。

mod a {

    pub fn foo_a_1(){
        //這樣是不行的,因為foo_b_2是private
        b::foo_b_2();
    }

    //修改:去掉pub
    fn foo_a_2(){
        println!("foo_a_2");
    }

    pub mod b {
        pub fn foo_b() {
            super::foo_a_2();
        }

        fn foo_b_2(){
            println!("foo_b_2");
        }
    }
}

fn main() {
    a::foo_a_1();
    a::b::foo_b();
}

這樣會報錯。

 

3.2 簡化訪問路徑

前面介紹過,main.rs就是cargo的入口,也可以理解為cargo的根,所以就本文的示例而言:

    a::b::foo_b();
    self::a::b::foo_b();
    crate::a::b::foo_b();

是等效的,就好比,文件d:\a\b\1.txt,如果我們當前已經在d:\根目錄下,
a\b\1.txt
d:\a\b\1.txt
.\a\b\1.txt
都能訪問。

用全路徑crate::a::b::foo_b()雖然能訪問,但是代碼看着太啰嗦了,可以用use來簡化:

mod a {
    fn foo_a_2(){
        println!("foo_a_2");
    }

    pub mod b {
        pub fn foo_b() {
            super::foo_a_2();
        }      
    }
}

use crate::a::b::foo_b;

fn main() {
    use crate::a::b::foo_b as x;
    foo_b();
    x();
}

運行效果一樣:

(base) ➜  demo_1 git:(master) ✗ cargo run
   Compiling demo_1 v0.1.0 (/Users/jimmy/code/demo_1)
    Finished dev [unoptimized + debuginfo] target(s) in 0.39s
     Running `target/debug/main`
foo_a_2
foo_a_2

從上面的示例可以看到:

  • use即可以在函數體內,也可以在函數外
  • 當2個模塊的函數有重名時,可以用use .. as .. 來取個別名 

 

3.3 將mod拆分到多個文件

如上圖,把mod a與b,分拆到a.rs, 及b.rs,與main.rs放在同1目錄。注意main.rs的首二行:

mod a;
mod b;

與常規mod不同的是,mod x后,並沒有{...}代碼塊,而是;號,rust會在同級目錄下,默認去找x.rs,再來看main方法:

fn main() {
   a::a::foo_a_2();
   b::b::foo_b();
}

為何這里是a::a:: 連寫2個a? 因為最開始聲明mod a; 這里面已有1個模塊a,而a.rs里首行,又定義了1個pub mod a,所以最終就是a::a::

如果mod太多,都放在一個目錄下,也顯得很亂,可以建個目錄,把mod放到該到目錄下:

(base) ➜  demo_1 git:(master) ✗ tree
.
├── Cargo.lock
├── Cargo.toml
└── src
    ├── abc
    │   ├── a.rs
    │   ├── b.rs
    │   └── mod.rs
    └── main.rs

這時要在該目錄下,新增1個mod.rs,用於聲明該目錄下有哪些模塊

pub mod a;
pub mod b;

然后b.rs中引用a模塊時,路徑也要有所變化:

pub mod b {
    use crate::abc::a::a::foo_a_2;
    pub fn foo_b() {
        foo_a_2();
    }     
}

main.cs里也要相應調整:

mod abc;

fn main() {
   abc::a::a::foo_a_2();
   abc::b::b::foo_b();
}

目錄abc,本身就視為1個mod,所以main.rs里的mod abc; 就是聲明abc目錄為1個mod,然后再根據abc/mod.rs,進一步找到a, b二個mod 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM