rust 模塊組織結構


rust有自己的規則和約定用來組織模塊,比如一個包最多可以有一個庫crate,任意多個二進制crate、導入文件夾內的模塊的兩種約定方式... 知道這些約定,就可以快速了解rust的模塊系統。
先把一些術語說明一下:

  • 是cargo的一個功能,當執行cargo new xxxx的時候就是創建了一個包。
  • crate是二進制或者庫項目。rust約定在Cargo.toml的同級目錄下包含src目錄並且包含main.rs文件,就是與包同名的二進制crate,如果包目錄中包含src/lib.rs,就是與包同名的crate。包內可以有多crate,多個crates就是一個模塊的樹形結構。如果一個包內同時包含src/main.rssrc/lib.rs,那么他就有兩個crate,如果想有多個二進制craterust約定需要將文件放在src/bin目錄下,每個文件就是一個單獨的crate
  • crate根用來描述如何構建crate的文件。比如src/main.rs或者src/lib.rs就是crate根crate根文件將由Cargo傳遞給rustc來實際構建庫或者二進制項目。
  • 帶有Cargo.toml文件的包用來描述如何構建crate,一個包可以最多有一個庫crate,任意多個二進制crate

github 代碼地址

模塊

模塊以mod開頭,下面創建了一個say模塊

mod say {
    pub fn hello() {
        println!("Hello, world!");
    }
}

需要注意的是模塊內,所有的項(函數、方法、結構體、枚舉、模塊和常量)默認都是私有的,可以用pub將項變為公有,上面的代碼里pub fn hello()就是把函數hello()變為公有的。
子模塊可以通過super訪問父模塊中所有的代碼,包括私有代碼。但是父模塊中的代碼不能訪問子模塊中的私有代碼。

mod say {
    pub fn hello() {
        println!("Hello, world!");
    }
    fn hello_2() {
        println!("hello")
    }
    pub mod hi {
        pub fn hi_1() {
            super::hello_2();
        }
        pub fn hi_2() {
            println!("hi there");
        }
    }
}

同一文件內的模塊

同一文件內的模塊,最外層的mod say不用設置為pub就可以訪問,但是mod say下面的要設置成pub才可以訪問。

fn main() {
    // 相對路徑
    say::hello();
    // 絕對路徑調用
    crate::say::hello();
    
    say::hi::hi_1();
    say::hi::hi_2();
}

mod say {
    pub fn hello() {
        println!("Hello, world!");
    }
    fn hello_2() {
        println!("hello")
    }
    pub mod hi {
        pub fn hi_1() {
            super::hello_2();
        }
        pub fn hi_2() {
            println!("hi there");
        }
    }
}

調用模塊內的方法,可以使用絕對路徑以crate開頭,也就是從crate根開始查找,say模塊定義在crate根 src/main.rs中,所以就可以這么調用crate::say::hello();絕對路徑類似於Shell中使用/從文件系統根開始查找文件。
相對路徑以模塊名開始say,他定義於main()函數相同的模塊中,類似Shell在當前目錄開始查找指定文件say/hello

mod hi是一個嵌套模塊,使用時要寫比較長say::hi::hi_2();,可以使用use將名稱引入作用域。

use crate::say::hi;

fn main() {
    // 相對路徑
    say::hello();
    // 絕對路徑調用
    crate::say::hello();

    // 不使用 use
    say::hi::hi_1();
    say::hi::hi_2();
    // 使用 use 后就可以這么調用
    hi::hi_1();
}

使用pub use 重導出名稱

不同的模塊之前使用use引入,默認也是私有的。如果希望調用的模塊內use引用的模塊,就要用pub公開,也叫重導出

fn main() {
    // 重導出名稱
    people::hi::hi_1();
    people::hello();
    // 但是不能 
    // people::say::hello();
}

mod say {
    pub fn hello() {
        println!("Hello, world!");
    }
    fn hello_2() {
        println!("hello")
    }
    pub mod hi {
        pub fn hi_1() {
            super::hello_2();
        }
        pub fn hi_2() {
            println!("hi there");
        }
    }
}

mod people {
    // 重導出名稱
    pub use crate::say::hi;
    use crate::say;
    pub fn hello() {
        say::hello();
    }
}

如果想都導出自己和嵌入的指定包可以用self,例mod people_2 把模塊people和嵌套模塊info全部導出來了。

use crate::say::hi;

fn main() {
    // 相對路徑
    say::hello();
    // 絕對路徑調用
    crate::say::hello();

    // 不使用 use
    say::hi::hi_1();
    say::hi::hi_2();
    // 使用 use 后就可以這么調用
    hi::hi_1();

    // 重導出名稱
    people::hi::hi_1();
    people::hello();
    // 但是不能 
    // people::say::hello();

    people_2::people::hello();
    people_2::info::name();
}

mod say {
    pub fn hello() {
        println!("Hello, world!");
    }
    fn hello_2() {
        println!("hello")
    }
    pub mod hi {
        pub fn hi_1() {
            super::hello_2();
        }
        pub fn hi_2() {
            println!("hi there");
        }
    }
}

pub mod people {
    // 重導出名稱
    pub use crate::say::hi;
    use crate::say;
    pub fn hello() {
        say::hello();
    }
    pub mod info {
        pub fn name() {
            println!("zhangsang");
        }
    }
}


mod people_2 {
    // 重導出名稱
    pub use crate::people::{self, info};
    pub fn hello() {
        info::name();
    }
}

不同文件夾的引用

方式一

看一下目錄結構:

rust的約定,在目錄下使用mod.rs將模塊導出。
看一下user.rs的代碼:

#[derive(Debug)]
pub struct User {
    name: String,
    age: i32
}

impl User {
    pub fn new_user(name: String, age: i32) -> User {
        User{
            name,
            age
        }
    }
    pub fn name(&self) -> &str {
        &self.name
    }
}

pub fn add(x: i32, y: i32) -> i32 {
    x + y 
}

然后在mod.rs里導出:

pub mod user;

main.rs調用

mod user_info;
use user_info::user::User;

fn main() {
    let u1 = User::new_user(String::from("tom"), 5);
    println!("user name: {}", u1.name());
    println!("1+2: {}", user_info::user::add(1, 2));
}

方式二

看一下目錄結構

和上面的不同之前是。這種方式是user_info目錄里沒有mod.rs,但是在外面有一個user_info.rs
user_info.rs中使用pub mod user;是告訴Rust在另一個與模塊同名的文件夾內(user_info文件夾)內加載模塊user。這也是rust的一個約定,但比較推薦用上面的方式。
代碼和上面是一樣的。
user.rs

#[derive(Debug)]
pub struct User {
    name: String,
    age: i32
}

impl User {
    pub fn new_user(name: String, age: i32) -> User {
        User{
            name,
            age
        }
    }
    pub fn name(&self) -> &str {
        &self.name
    }
}

pub fn add(x: i32, y: i32) -> i32 {
    x + y 
}

user_info.rs里導出

pub mod user;

main.rs調用

mod user_info;
use user_info::user::User;

fn main() {
    let u1 = User::new_user(String::from("tom"), 5);
    println!("user name: {}", u1.name());
    println!("1+2: {}", user_info::user::add(1, 2));
}

使用外部包

使用外部包,一般就是從crates.io下載,當然也可以自己指寫下載地點,或者使用我們本地的庫,或者自建的的倉庫。

一般方式

Cargo.tomldependencies下寫要導入的依賴庫

[dependencies]
regex = "0.1.41"

運行cargo build會從crates.io下載依賴庫。
使用的時候,直接使用use引入

use regex::Regex;

fn main() {
    let re = Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap();
    println!("Did our date match? {}", re.is_match("2014-01-01"));
}

指定庫地址

除了crates.io下載依賴庫,也可以自己指定地址,也可以指定branch tag commit,比如下面這個

[dependencies]
# 可以和包不同名,也可以同名
my_rust_lib_1={package="my_lib_1",git="ssh://git@github.com/lpxxn/my_rust_lib_1.git",tag="v0.0.2"}

就是從github.com/lpxxn/my_rust_lib_1上下載包。也可以使用https

my_rust_lib_1={package="my_lib_1",git="https://github.com/lpxxn/my_rust_lib_1.git",branch="master"}

執行cargo build就會自動下載,使用的時候也是一樣的。

use my_rust_lib_1;
fn main() {
    println!("Hello, world!");
    println!("{}", my_rust_lib_1::add(1, 2));
    let u = my_rust_lib_1::User::new_user(String::from("tom"), 2);
    println!("user: {:#?}", u);
}

使用本地的庫

我們新建一個二進制庫項目

cargo new pkg_demo_3

然后在pkg_demo_3內建一個庫項目

cargo new --lib utils

然后就可以在 utils里寫我們的庫代碼了
看一下現在的目錄結構

utils庫的user.rs里還是我們上面的代碼

#[derive(Debug)]
pub struct User {
    name: String,
    age: i32
}

impl User {
    pub fn new_user(name: String, age: i32) -> User {
        User{
            name,
            age
        }
    }
    pub fn name(&self) -> &str {
        &self.name
    }
}

pub fn add(x: i32, y: i32) -> i32 {
    x + y 
}

lib.rs里對user模塊導出

pub mod user;
pub use user::User;

然后在我們的二進制庫的Cargo.toml引入庫

[dependencies]
utils = { path = "utils", version = "0.1.0" }

path就是庫項目的路徑
main.rs使用use引入就可以使用了

use utils::User;

fn main() {
    let u = User::new_user(String::from("tom"), 5);
    println!("user: {:#?}", u);
}

自建私有庫

除了crates.io也可以自建registrie。這個有時間再重新寫一篇帖子單獨說,可以先看一下官方文檔。
官方文檔:registrie
依賴官方文檔
帖子 github 代碼地址


免責聲明!

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



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