Rust 项目结构详解

396 阅读4分钟

前沿

这是 rust 开发环境搭建的第二篇,深入理解 rust 项目的组织结构,掌握 cargo 等工具的使用,是进入 rust programming 世界的关键步骤。

Cargo 项目结构

Cargo 是 rust 用来管理项目的核心工具,所有的 rust 项目都是基于 cargo 工具来构建的,下面是一个只包含最核心目录和文件的一个组织结构:

.
├── Cargo.lock
├── Cargo.toml
├── src
│   ├── bin
│   │   └── another_executable.rs
│   ├── lib.rs
│   └── main.rs
├── benches
│   └── large-input.rs
├── examples
│   └── simple.rs
└── tests
    └── some-integration-tests.rs

解释:

src 目录中存放所有的 rust 源码

src/lib.rs 是默认的库文件入口代码

src/main.rs 是默认的可执行文件入口代码

编译所生成的可执行文件的实现代码都在 src/bin/*.rs

后面三个目录 benches/, examples/, tests/ 都是辅助开发的一些源码目录,大型项目完整性需要。

参考这里

 注意,引用某个 mod 时(比如 something),可以是 something.rssomething/mod.rs,这有点类似 lua 中的引用方式。

src/something.rs
pub trait Foo {
    fn method(&self) -> String;
}

impl Foo for u8  {
    fn method(&self) -> String { format!("u8: {}", *self) }
}

impl Foo for String {
    fn method(&self) -> String { format!("string: {}", *self) }
}
src/main.rs
mod something;
use crate::something::*;

fn do_something<T: Foo>(x :T) {
    x.method();
}

fn main() {
    let x = 5u8;
    let y = "Hello ".to_string();

    do_something(x);
    do_something(y);
}

解释: src/lib.rssrc/main.rs 都是对外的,两者不互相关联和引用,这两个文件名是固定的,不要改动。

如果想要多模块开发,比如 main.rs 中有些函数想独立出来文件,那么就新建一些文件或目录,比如,把函数移动到 something.rs 中去,然后,这个文件就作为一个 mod,想要在 main.rs 中引用 something.rs 中的函数时,在 main.rs 文件中做两步:

(1)声明要使用的 module 名字,如下: mod something;

(2)声明要使用的 module 中的那些函数,如下: use crate::something::*;

同样,src/lib.rs 也是对外的,因此,它只需要做第一步就可以,但因为是要对外提供接口,因此 lib.rs 中一定要加 pub。

pub mod something
记住:

src/main.rs 不要引用 src/lib.rs 文件。因为他们都是提供对外访问的,一个提供 library,一个提供 binary,彼此之间是独立的,无任何依赖关系。

当然,如果你不需要提供 libaray,那么你可以不创建 lib.rs。由于 lib.rs 是对外提供的,所以 subdirectory 也可以用 use crate::something 来访问。

src 下的目录结构:兄弟、父、子三个层级

crate root 很重要,他就是项目的根目录,所有人都可以引用根目录下的 crate。

use 语句与 C++ 的语句作用相同,作用就是:rename and bring into scope

简单来说就是,使用了 use 之后,可以免去前缀来使用了,比如:

C++ 中

没使用 use 语句之前:

std::cout << "hello world!" << std::endl;

使用了 use 语句之后:

use std;
cout << "hello world!" << endl;

同理,rust 中

enum Status {
    Rich,
    Poor,
}

fn main() {
    use Status::{Poor, Rich};
    
    //let status = Status::Poor;//无需显示的加上作用域Status
    let status = Poor;
}

pub mod a 
{
    pub mod b
    {
        pub fn function()
        {
            println!("This is a::b::function");
        }

        pub fn other_funtion()
        {
            println!("This is a::b::other_funtion");
        }
    }
}

use a::b as ab;
use a::b::other_funtion as ab_other_funtion;

fn main()
{
    ab::function();
    ab::other_funtion();
    ab_other_funtion();
}

注意: 需要很好的理解 mod 关键词的 rename 含义,也就是说,当你使用 pub mod xxx 时,你其实是对一个模块命名了,命名之后,你就可以使用 use crate::xxx::func1 或 use self:xxx:func1 这种方式了;而如果没有使用 pub mod xxx 的话,那么你就只能使用 use crate::filename::func1 来引用了,如果这个 filename 是有多级目录的话,会很麻烦(路径会很长),所以,使用 mod 就可以把一个目录下所有的子目录都定义成一个 module,这样,引用的时候路径就简化了。

比如有一个 project 结构如下:

// 1)a.rs
pub fn a_echo(){
    println!("a_echo!");

//(2)c.rs
注意,这里 crate 不能用 self
use crate::src_a::a::*;
pub fn c_echo(){
    println!("c_echo!");
    a_echo();
}
    
// (3) b.rs
use crate::src_a::a_echo;
pub fn b_echo(){
    println!("b_echo! => call a()!");
    a_echo();
}
    
// (4) src_a.rs
pub mod a;
pub mod c;
pub use a::*;
pub use c::*;
    
// (5) src_b.rs
pub mod b;
pub use b::*;

// (6) main.rs
crateself 可以互相替代。
pub mod src_a;
pub mod src_b;
pub use self::src_a::*;
pub use crate::src_b::*;

fn main() {
    println!("Hello, world!");
    src_a::a_echo();
    src_b::b_echo();
}

可以看到,src_a.rs 和 src_b.rs 的作用就是把内部的两个文件定义成一个 mod 了,这样,在上层 parent 的文件就可以方便引用了。 参考:blog.csdn.net/wowotuo/art…

  1. 可以用 use crate::  把 absolute path 引入
mod sound {
    pub mod instrument {
        pub fn clarinet() {
        // Function body code goes here
        }
    }
}
use crate::sound::instrument;
fn main() {
    instrument::clarinet();
    instrument::clarinet();
    instrument::clarinet();
}
  1. 也可以用 use self:: 把 relative path 引入
mod sound {
    pub mod instrument {
        pub fn clarinet() {
        // Function body code goes here
        }
    }
}
use self::sound::instrument;
fn main() {
    instrument::clarinet();
    instrument::clarinet();
    instrument::clarinet();
}

ABSOLUTE VS RELATIVE PATHS WITH “USE” :

mod sound {
    pub mod instrument {
        pub fn clarinet() {
        // Function body code goes here
        }
    }
}
mod performance_group {
    use crate::sound::instrument;
    pub fn clarinet_trio() {
        instrument::clarinet();
        instrument::clarinet();
        instrument::clarinet();
    }
}

fn main() {
    performance_group::clarinet_trio();
}

mod 与 C++ 中的 namespace 关键词相同作用,用来定义一个作用域 区别在于,C++ 中 namespace 是在原文件中定义使用,而 rust 则可以在引用的文件中使用。

相关参考:

blog.csdn.net/yzpbright/a…

全文完!

如果你喜欢我的文章,欢迎关注我的微信公众号:codeandroad