浅谈Rust包和模块管理

在谈Rust中项目模块的组织方式之前,先聊一下三个概念

  • Package
  • Create
  • Module

这里我就不把上面三个单词翻译成中文了,感觉没有必要,我们以实操来解释一下这三个概念就好

Package

Package 其实就是一个项目,是 cargo 提供让我们创建,测试和分享的

二进制Package

当我们输入 cargo new hello 会发现提示:Created binary (application) hello package。

我们通过cargo创建的hello,其实就是一个package

go 复制代码
$ cargo new hello
     Created binary (application) `hello` package

同时在根目录创建了Cargo.toml文件,该文件里面并没有提到 src/main.rs 作为程序的入口,原因是 Cargo 有一个惯例:src/main.rs 是二进制包的根文件 ,该文件编译之后的名字和我们创建的Package名字相同,在这里都是hello,所有代码都是从该文件的main函数开始执行

ini 复制代码
[package]
name = "hello"
version = "0.1.0"
edition = "2021"
​
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
​
[dependencies]

我们使用cargo run看一下编译之后的结果

arduino 复制代码
$ cargo run
      hello world

我们可以看到有一个名为hello 的二进制文件,我们直接运行它,也会直接输出hello world

shell 复制代码
$ ./hello
      hello world

库Package

我们再来创建一个库类型的Package

go 复制代码
$ cargo new my_lib --lib
     Created library `my_lib` package

但是如果你尝试使用cargo run来运行,会直接报错

arduino 复制代码
$ cargo run
error: a bin target must be available for `cargo run`

原因是库类型的 Package 只能作为三方库被其它项目引用,而不能独立运行,只有之前的二进制 Package 才可以运行。

src/main.rs 一样,Cargo 知道,如果一个 Package 包含有 src/lib.rs,意味它包含有一个库类型的同名包 myLib,该包的根文件是 src/lib.rs

Crate

Crate其实就是我们编译的基本单位,它也分成两种:可执行Crate和库Crate,如果和上面Package两种类型对应的话,我觉得叫binary(application) Cratelibrary Crate 可能会好理解一点

可执行Crate

刚刚我们创建的hello项目下的main.rs其实就是一个可执行Crate文件,可以直接通过rustc来编译

css 复制代码
$ rustc main.rs

库Crate

就拿我们刚刚创建的库Package myLib举例子,src/lib.rs就是一个库Crate

库Crate并没有 main 函数,它们也不会编译为可执行程序,它们提供一些诸如函数之类的东西,使其他项目也能使用这些东西。

rust 复制代码
// src/lib.rs
pub fn add(left: usize, right: usize) -> usize {
    left + right
}
​
#[cfg(test)]
mod tests {
    use super::*;
​
    #[test]
    fn it_works() {
        let result = add(2, 2);
        assert_eq!(result, 4);
    }
}

我们使用cargo build发现生成了两个libmy_lib的文件,为什么叫libmy_lib呢?因为这是library Crate,所以叫lib,还记得我们的Package名字叫my_lib吗,所以文件名就叫libmy_lib了。与 src/main.rs 一样,Cargo 知道,如果一个 Package 包含有 src/lib.rs,意味它包含有一个库类型的同名包 my_lib,该包的根文件是 src/lib.rs

同时Rust规定,一个Package只会包含有一个或零个库Crate

如果我们想改默认的打包名呢?详见:doc.rust-lang.org/cargo/refer...

ini 复制代码
[lib]
name = "foo"           # The name of the target.
path = "src/lib.rs"    # The source file of the target.
test = true            # Is tested by default.
doctest = true         # Documentation examples are tested by default.
bench = true           # Is benchmarked by default.
doc = true             # Is documented by default.
plugin = false         # Used as a compiler plugin (deprecated).
proc-macro = false     # Set to `true` for a proc-macro library.
harness = true         # Use libtest harness.
edition = "2015"       # The edition of the target.
crate-type = ["lib"]   # The crate types to generate.
required-features = [] # Features required to build this target (N/A for lib).

Package和Crate规则

我们暂时先来总结一下

  • 一个Package包含零个或一个库Crate

这点我们上面已经说过了,就不提拉~

  • 一个Package可以包含任意多个可执行Crate

这个当然可以呀!src/main.rs只是默认的可执行Crate,我们还可以新建别的可执行Crate文件

比如我们在上述的HelloProject中新建一个src/bin/test.rs

arduino 复制代码
// src/bin.test.rs
fn main() {
    println!("Hello, test!");
}
​
$ 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: hello, test

它会提示我们有两个可执行的Crate文件,分别是hello和test,我们需要去指定哪个文件

scss 复制代码
$ cargo run --bin test
   Compiling hello v0.1.0 (/Users/chenwenliang/rust/hello)
    Finished dev [unoptimized + debuginfo] target(s) in 0.49s
     Running `target/debug/test`
Hello, test!
  • 一个Package至少包含一个库Crate或者可执行Crate

这条规则也挺好理解的,我们之前提到Package有两种类型,分别是二进制Package和库Package,对应的就是可执行Crate和库 Crate,说明Package必须是这两种类型中的一种

Module

我们讲完PackageCrate关系,接一下讲讲CrateModule关系

一个Crate可以有多个Module,通过mod关键字可以声明Module

调用mod

rust 复制代码
// src/main.rs
​
mod hello {
    pub fn hi() {
        println!("hi");
    }
​
    pub fn bye() {
        println!("bye")
    }
​
    pub mod say {
        pub fn morning() {
            println!("say morning")
        }
​
        pub fn night() {
            println!("say night")
        }
    }
}
​
fn main() {
    hello::bye();
    // crate::hello::bye(); 也可以这么调用
    hello::say::morning();
    hello::hi();
    hello::say::night();
    println!("Hello, world!");
}

mod间相互调用

  1. 可以通过crate关键字,相当于从绝对路径开始找
rust 复制代码
mod good {
    pub fn log() {
        // 调用hello mod中的函数
        crate::hello::bye();
    }
}
  1. 使用相对路径,从当前模块开始,以 selfsuper 或当前模块的标识符开头

    • self: 自身
    • super: 父级
rust 复制代码
mod hello {
    pub fn hi() {
        println!("hi");
    }
​
​
    pub mod say {
        pub fn good() {
          // 自身mod
          self::hi();
      }
        
        pub fn hi() {
            // super代表上一级mod
            super::hi();
        }
    }
}

不同Crate中mod调用

css 复制代码
// src/main.rs
​
mod good
​
fn main() {
    good::say_good();
}

我们可以在main.rs的同级目录下新建一个good.rs文件,或者同级目录下新建good/mod.rs

因为main.rs中发现了声明mod good,它会在同级目录下去找good.rs文件,或者去找good文件下的mod.rs文件

rust 复制代码
// src/good/mod.rs
// good.rs
​
pub fn say_good() {
    println!("good");
}

注: 如果通过文件夹的方式,必须要以mod.rs命令,它会默认从这两个路径找

注:不同Crate引用要声明pub,不然会访问失败

调用library中的mod

我们怎么调用library Crate中的函数呢?我们知道lib默认的名字和Package的名字一样,所以我们直接使用Package的名字就可以调用library Crate中相应的函数

rust 复制代码
// src/lib.rs
pub fn lib_hello() {
    println!("lib hello")
}
​
// src/main.rs
fn main() {
    hello::lib_hello();
    println!("Hello, world!");
}

总结

相关推荐
SomeB1oody1 天前
【Rust自学】4.1. 所有权:栈内存 vs. 堆内存
开发语言·后端·rust
SomeB1oody2 天前
【Rust自学】4.2. 所有权规则、内存与分配
开发语言·后端·rust
SomeB1oody2 天前
【Rust自学】4.5. 切片(Slice)
开发语言·后端·rust
编码浪子2 天前
构建一个rust生产应用读书笔记6-拒绝无效订阅者02
开发语言·后端·rust
baiyu332 天前
1小时放弃Rust(1): Hello-World
rust
baiyu332 天前
1小时放弃Rust(2): 两数之和
rust
Source.Liu2 天前
数据特性库 前言
rust·cad·num-traits
编码浪子2 天前
构建一个rust生产应用读书笔记7-确认邮件1
数据库·rust·php
SomeB1oody2 天前
【Rust自学】3.6. 控制流:循环
开发语言·后端·rust
Andrew_Ryan2 天前
深入了解 Rust 核心开发团队:这些人如何塑造了世界上最安全的编程语言
rust