跳过正文

包和模块:package/crate/module

··1958 字
Rust
rust-lang - 这篇文章属于一个选集。
§ 14: 本文

cargo 项目也称为 package, 一个 package 包含一个或多个 crate, crate 分为 3 种类型: binary、library、 procedure_macro。

使用 cargo new my-project 命令创建 package,一个 package 必须包含一个 library 或 binnary crate,至多包含一个 libraray crate, 可以包含任意数量的 binary crate(src/bin/ 目录下每个文集对应一个 binary crate)。

crate 是 Rust 的编译、发布、版本化、加载的单元, 其中 crate root 是 Rust 编译器开始编译的文件, 也即 crate 的根 module;

  • src/main.rs: binary crate 的 crate root,crate 名称与 package 名称相同;
  • src/lib.rs: library crate 的 crate root(一个 package 包含 0-1 个 library crate),crate 名称与 package 名相同;
  • use 或 pub 使用 crate 标识符来引用 root crate module 中的 item,如 use crate::xxx 引用 crate root 下的 xxx item;
# 创建 bin crate
cargo new foo

# 创建 lib crate
cargo new --lib bar

.
├── bar
│   ├── Cargo.toml
│   └── src
│       └── lib.rs
└── foo
├── Cargo.toml
└── src
└── main.rs

# bin 目录下的各文件为独立的 binary, 可以通过 cargo 的 --bin my_other_bin 来指定

crate file 可以使用 mod 来引用其它 module 文件中 item, 该 mod 可以是单独的文件或当前文件中定义, mod 可以嵌套。

  • src/<module_name>.rs // module 单文件
  • src/<module_name>/mod.rs // module 目录,该目录下必须有一个 mod.rs 文件。

父 module 的 item, 不管是否 public, 都可以在自身和子 module 中使用,但是不能在父 module (递归向上) 和其它非子 module 中使用。

在 Rust 2015 以后版本, 基本上不需要再使用 extern crate xx 了, 因为编译器从 Cargo.toml 获得依赖的外部 crate 列表。但是 Rust 自带的 crate 如 alloc/test/proc_macro 只能 extern crate 来声明:

extern crate alloc;
use alloc::rc::Rc;

crate 名称是标识符, 不支持短横杠,但 Cargo package 名称可以包含短横杠,所以 Cargo.toml 中指定的 package name 可以包含短横杠,但在使用 extern crateuse 来引用 package 中的 crate 时,需要将短横杠替换为下划线:

extern crate hello_world;

extern crate foo as _ // 表示不使用 foo crate 中的 item,只做链接

module 引入了一级 namespace, 其中的 item 需要使用 module::item 来访问.

  • module 是在一个 crate 内部,用于将代码进行分组;
  • 增加可读性,易于复用;
  • 控制项目(item)的可见性。
  • module 可以嵌套,可以包含其它 item,如 struct/enum/常量/trait/函数等定义等;

module 中 item 的可见性:

  • pub fn: 只对当前 module 和子 module 开放;
  • pub (in path::to::module): pub(in crate::my_mod), 对指定的 crate 开放;
  • pub (self):: 只对当前 module 开放; 等效于不加 pub;
  • pub (super):: 只对父 module 开放;
  • pub(crate):: 对当前 crate 开放;
mod my_mod {
    // 缺省是 private
    fn private_function() {
        println!("called `my_mod::private_function()`");
    }

    // 使用 pub 来开放可见性
    pub fn function() {
        println!("called `my_mod::function()`");
    }

    pub fn indirect_access() {
        print!("called `my_mod::indirect_access()`, that\n> ");
        private_function();
    }

    // module 可以嵌套
    pub mod nested {
        pub fn function() {
            println!("called `my_mod::nested::function()`");
        }

        #[allow(dead_code)]
        fn private_function() {
            println!("called `my_mod::nested::private_function()`");
        }

        pub(in crate::my_mod) fn public_function_in_my_mod() {
            print!("called `my_mod::nested::public_function_in_my_mod()`, that\n> ");
            public_function_in_nested();
        }

        pub(self) fn public_function_in_nested() {
            println!("called `my_mod::nested::public_function_in_nested()`");
        }

        pub(super) fn public_function_in_super_mod() {
            println!("called `my_mod::nested::public_function_in_super_mod()`");
        }
    }

    pub fn call_public_function_in_my_mod() {
        print!("called `my_mod::call_public_function_in_my_mod()`, that\n> ");
        nested::public_function_in_my_mod();
        print!("> ");
        nested::public_function_in_super_mod();
    }

    pub(crate) fn public_function_in_crate() {
        println!("called `my_mod::public_function_in_crate()`");
    }

    mod private_nested {
        #[allow(dead_code)]
        pub fn function() {
            println!("called `my_mod::private_nested::function()`");
        }

        #[allow(dead_code)]
        pub(crate) fn restricted_function() {
            println!("called `my_mod::private_nested::restricted_function()`");
        }
    }
}


fn main() {
    // Modules allow disambiguation between items that have the same name.
    function();
    my_mod::function();

    // Public items, including those inside nested modules, can be
    // accessed from outside the parent module.
    my_mod::indirect_access();
    my_mod::nested::function();
    my_mod::call_public_function_in_my_mod();

    // pub(crate) items can be called from anywhere in the same crate
    my_mod::public_function_in_crate();
}

module 可以通过 #[path] attribute 来指定它的文件路径, module 也可以使用 #[cfg_attr] 来指定在 match 条件的情况下,为 module 添加一些 attr,比如 path attr:

#[path = "thread_files"]
mod thread {
    // Load the `local_data` module from `thread_files/tls.rs` relative to this source file's
    // directory.
    #[path = "tls.rs"]
    mod local_data;
}

mod inline {
    #[path = "other.rs"]
    mod inner;
}

// 在 target_os = "linux" 的情况下,添加 #[path = "linux.rs"] 属性,表示西面 mod os 的文件是 linux.rs;
#[cfg_attr(target_os = "linux", path = "linux.rs")]
#[cfg_attr(target_os = "linux", cfg_attr(feature = "multithreaded", some_other_attribute))]
#[cfg_attr(windows, path = "windows.rs")]
mod os;

#[cfg_attr(feature = "magic", sparkles, crackles)]
fn bewitched() {}
// 等效于:在 feature = "magic" 的情况下,添加下面两个 attr
#[sparkles]
#[crackles]
fn bewitched() {}

item path:为了使用 Rust 模块中的 item,需要使用 :: 风格的路径.

路径有两种形式:

  1. 绝对路径:从 crate root 开始,使用 use crate::module::item;
    • crate 表示本 crate 的根 module,如 lib.rs 或 bin.rs 中的 item;
  2. 相对路径:从当前 module 开始,使用 self/super 或当前 module 的标识符;

use: 将某个标识符和一个 full path 绑定, 后续可以直接使用标识符:

  • use xx::yy 中的 xx 是相对于当前 module 的, 可以是子 module 或 item, 如果都不存在则 xx 是 crate 名称;
  • use self::item 导入当前 module 的 item;
  • yse super::item 导入父 module 的 item;
  • use crate::module::item 导入从 crate root 开始的 module 的 item(绝对路径)
  • 开头表示使用外部 crate image, 而不会把 image 作为 module name= ;
  • use crate::module_1::module_2::*; 引入 module_2 下的所有 item;
  • use super::item as sitem; 使用 as 指定的标识符 sitem 来引用 super::item;
  • use a::b::{self as ab, c as abc}; use a::b::{self as ab, c, d::{*, e::f}};
  • use super::item as _; 不使用 super::item, 只是会链接该 crate package;
use crate::deeply::nested::{
    my_first_function,
    my_second_function,
    AndATraitType
};

fn main() {
    my_first_function();
}

// 使用 use..as 对绑定重命名
use deeply::nested::function as other_function;
fn function() {
    println!("called `function()`");
}
mod deeply {
    pub mod nested {
        pub fn function() {
            println!("called `deeply::nested::function()`");
        }
    }
}
fn main() {
    other_function();
    println!("Entering block");
    {
        use crate::deeply::nested::function;
        function();
        println!("Leaving block");
    }
    function();
}

// 使用 pub use 将绑定在当前 create module 重新 export
pub use deeply::nested::function as other_function;

pub use 将 item 在本 moudule 中重新导出, 这样其他 crate 导入该 module 时也可以使用这些 item. 常见的情况是将嵌套很深的 item 通过 pub use 在 crate root module 导出,这样使用者就不需要使用很长、很深的 use path 到导入该 item.

pub use 导出的 item 在 module 的 cargo doc 中的 “Reexports” 部分展示;

缺省情况下, Rust 标准库被自动导入到 crate root module,可以使用 std 来引用它中的 item。同时隐式的为 std 使用 macro_use attribute 来导入 std 库中 macro_export 的所有宏。同时 core 也被导入到 crate root module。

通过在 crate level 添加 #![no_std] attr 可以避免上面隐式自动导入 std 和它的 macro,而只会导入 core create 以及他的 macro。

  • 使用 #![no_std] attr 只是关闭了自动导入 std 和它的 macro,代码还是可以使用 extern crate std; 来显式导入和链接 Rust 标准库。

注:可以使用 https://github.com/dtolnay/cargo-expand 来展开 macro 代码:

  • 也可以使用 cargo build --verbose 来看到展开的代码:
// cat src/main.rs
#[derive(Debug)]
struct S;

fn main() {
    println!("{:?}", S);
}

// 展开: cargo expand
#![feature(prelude_import)]
#[prelude_import]
use std::prelude::v1::*; // 自动导入了 std::prelude
#[macro_use]
extern crate std; // 自动导入了 std crate 和其中 export 的 macro

struct S;
#[automatically_derived]
#[allow(unused_qualifications)]
impl ::core::fmt::Debug for S {
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match *self {
            S => {
                let mut debug_trait_builder = f.debug_tuple("S");
                debug_trait_builder.finish()
            }
        }
    }
}
fn main() {
    {
        ::std::io::_print(::core::fmt::Arguments::new_v1(
            &["", "\n"],
            &match (&S,) {
                (arg0,) => [::core::fmt::ArgumentV1::new(arg0, ::core::fmt::Debug::fmt)],
            },
        ));
    };
}
rust-lang - 这篇文章属于一个选集。
§ 14: 本文

相关文章

不安全:unsafe
··824 字
Rust
Rust
借用:refer/borrow
··3120 字
Rust
Rust 引用类型和借用
内置类型:type
··16432 字
Rust
Rust 内置基本类型介绍
函数、方法和闭包:function/method/closure
··6964 字
Rust
Rust 函数、方法和闭包