《通过例子学Rust》第10章 模块

Rust 提供了一套强大的模块(module)系统,可以将代码按层次分成多个逻辑单元(模块),并管理这些模块之间的可见性(公有(public)或私有(private))。

模块是项(item)的集合,项可以是:函数,结构体,trait,impl 块,甚至其它模块。

10.1 可见性

默认情况下,模块中的项拥有私有的可见性(private visibility),不过可以加上 pub 修饰语来重载这一行为。模块中只有公有的(public)项可以从模块外的作用域访问。

复制代码
// 10.1节 可见性

// 一个名为 `my_mod` 的模块
mod my_mod {
    // 模块中的项默认具有私有的可见性
    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();
    }

    // 模块也可以嵌套
    pub mod nested {
        pub fn function() {
            println!("called 'my_mod::netsted::function()'");
        }

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

        // 使用 `pub(in path)` 语法定义的函数只在给定的路径中可见。
        // `path` 必须是父模块(parent module)或祖先模块(ancestor module)
        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)` 语法定义的函数则只在当前模块中可见。
        pub(self) fn public_function_in_nested() {
            println!("called 'my_mod::nested::public_function_in_nested");
        }

        // 使用 `pub(super)` 语法定义的函数只在父模块中可见。
        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)` 使得函数只在当前 crate 中可见
    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()'");
        }
    }
}

fn function() {
    println!("called 'function()'");
}

fn main() {
    // 模块机制消除了相同名字的项之间的歧义。
    function();
    my_mod::function();

    // 公有项,包括嵌套模块内的,都可以在父模块外部访问。
    my_mod::indirect_access();
    my_mod::nested::function();
    my_mod::call_public_function_in_my_mod();

    // pub(crate) 项可以在同一个 crate 中的任何地方访问
    my_mod::public_function_in_crate();

    // pub(in path) 项只能在指定的模块中访问
    // 报错!函数 `public_function_in_my_mod` 是私有的
    //my_mod::nested::public_function_in_my_mod();
    // 试一试 ^ 取消该行的注释

    // 模块的私有项不能直接访问,即便它是嵌套在公有模块内部的

    // 报错!`private_function` 是私有的
    //my_mod::private_function();
    // 试一试 ^ 取消此行注释

    // 报错!`private_function` 是私有的
    //my_mod::nested::private_function();
    // 试一试 ^ 取消此行的注释

    // Error! `private_nested` is a private module
    //my_mod::private_nested::function();
    // 试一试 ^ 取消此行的注释
    println!("Hello Rust"); 
}

// rustc main.rs
// ./main

编译运行:

复制代码
PS F:\rustproject\rustbyexample\chapter10\example10_1> rustc main.rs
PS F:\rustproject\rustbyexample\chapter10\example10_1> ./main
called 'function()'
called 'my_mod::function()'
called 'my_mod::indirect_access()', that
called 'my_mod::private_function()'
called 'my_mod::netsted::function()'
called 'my_mod::call_public_function_in_my_mod()', that
> called 'my_mod::nested::public_function_in_my_mod()', that
 >called 'my_mod::nested::public_function_in_nested
> called my_mod::nested::public_function_in_super_mod
called 'my_mod::public_function_in_crate()
Hello Rust
PS F:\rustproject\rustbyexample\chapter10\example10_1>

10.2 结构体的可见性

结构体的字段也是一个可见性的层次。字段默认拥有私有的可见性,也可以加上 pub 修饰语来重载该行为。只有从结构体被定义的模块之外访问其字段时,这个可见性才会起作用,其意义是隐藏信息(即封装,encapsulation)。

复制代码
// 10.2节 结构体的可见性

mod my {
    // 一个公有的结构体,带有一个公有的字段(类型为泛型 `T`)
    pub struct OpenBox<T> {
        pub contents: T,
    }

    // 一个公有的结构体,带有一个私有的字段(类型为泛型 `T`)
    #[allow(dead_code)]
    #[derive(Debug)]
    pub struct ClosedBox<T> {
        contents: T,
    }

    impl<T> ClosedBox<T> {
        // 一个公有的构造器方法
        pub fn new(contents: T) -> ClosedBox<T> {
            ClosedBox {
                contents: contents,
            }
        }
    }
}

fn main() {
    // 带有公有字段的公有结构体,可以像平常一样构造
    let open_box = my::OpenBox{contents: "public information"};

    // 并且它们的字段可以正常访问到。
    println!("The open box contains: {}", open_box.contents);

    // 带有私有字段的公有结构体不能使用字段名来构造。
    // 报错!`ClosedBox` 含有私有字段。
    //let closed_box = my::ClosedBox { contents: "classified information" };
    // 试一试 ^ 取消此行注释

    // 不过带有私有字段的结构体可以使用公有的构造器来创建。
    let _closed_box = my::ClosedBox::new("classified information");
    println!("_closed_box = {:?}", _closed_box);

    // 并且一个结构体中的私有字段不能访问到。
    // 报错!`content` 字段是私有的。
    //println!("The closed box contains: {}", _closed_box.contents);
    // 试一试 ^ 取消此行注释    

    println!("Hello Rust"); 
}

// rustc main.rs
// ./main

编译运行:

复制代码
PS F:\rustproject\rustbyexample\chapter10\example10_2> rustc main.rs
PS F:\rustproject\rustbyexample\chapter10\example10_2> ./main
The open box contains: public information
_closed_box = ClosedBox { contents: "classified information" }
Hello Rust
PS F:\rustproject\rustbyexample\chapter10\example10_2>
参见:

泛型方法

10.3 use声明

use 声明可以将一个完整的路径绑定到一个新的名字,从而更容易访问。

复制代码
// 10.3节 声明
// 将 `deeply::nested::function` 路径绑定到 `other_function`。
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() {
    // 更容易访问 `deeply::nested::funcion`
    other_function();

    println!("Entering block");
    {
        // 这和 `use deeply::nested::function as function` 等价。
        // 此 `function()` 将遮蔽外部的同名函数。
        use deeply::nested::function;
        function();

        // `use` 绑定拥有局部作用域。在这个例子中,`function()`
        // 的遮蔽只存在这个代码块中。
        println!("Leaving block");
    }

    function();

    println!("Hello Rust"); 
}

// rustc main.rs
// ./main

编译运行:

复制代码
PS F:\rustproject\rustbyexample\chapter10\example10_3> rustc main.rs
PS F:\rustproject\rustbyexample\chapter10\example10_3> ./main
called 'deeply::nested::function()'
Entering block
called 'deeply::nested::function()'
Leaving block
called 'function()'
Hello Rust
PS F:\rustproject\rustbyexample\chapter10\example10_3>

10.4 super和self

可以在路径中使用 super (父级)和 self(自身)关键字,从而在访问项时消除歧义,以及防止不必要的路径硬编码。

复制代码
// 10.4节 super和self

fn function() {
    println!("called 'function()'");
}

mod cool {
    pub fn function() {
        println!("called 'cool::function()'");
    }
}

mod my {
    fn function() {
        println!("called 'my::function()'");
    }

    mod cool {
        pub fn function() {
            println!("called 'my::cool:function()'");
        }
    }

    pub fn indirect_call() {
        // 让我们从这个作用域中访问所有名为 `function` 的函数!
        print!("called 'my::indirect_call()', that\n> ");

        // `self` 关键字表示当前的模块作用域------在这个例子是 `my`。
        // 调用 `self::function()` 和直接调用 `function()` 都得到相同的结果,
        // 因为他们表示相同的函数。
        self::function();
        function();

        // 我们也可以使用 `self` 来访问 `my` 内部的另一个模块:
        self::cool::function();

        // `super` 关键字表示父作用域(在 `my` 模块外面)。
        super::function();

        // 这将在 *crate* 作用域内绑定 `cool::function` 。
        // 在这个例子中,crate 作用域是最外面的作用域。
        {
            use crate::cool::function as root_function;
            root_function();
        }
    }
}

fn main() {
    my::indirect_call();  

    println!("Hello Rust"); 
}
 
// rustc main.rs
// ./main

编译运行:

复制代码
PS F:\rustproject\rustbyexample\chapter10\example10_4> rustc main.rs
PS F:\rustproject\rustbyexample\chapter10\example10_4> ./main
called 'my::indirect_call()', that
> called 'my::function()'
called 'my::function()'
called 'my::cool:function()'
called 'function()'
called 'cool::function()'
Hello Rust
PS F:\rustproject\rustbyexample\chapter10\example10_4>

10.5 文件分层

模块可以分配到文件/目录的层次结构中。让我们将《可见性》一节中的例子的代码拆分到多个文件中:

复制代码
$ tree .
.
|-- my
|   |-- inaccessible.rs
|   |-- mod.rs
|   `-- nested.rs
`-- split.rs

split.rs 的内容:

复制代码
// 10.5节 文件分层
// 此声明将会查找名为 `my.rs` 或 `my/mod.rs` 的文件,并将该文件的内容放到
// 此作用域中一个名为 `my` 的模块里面。
mod my;

fn function() {
    println!("called 'function()'");
}
 
fn main() {
    my::function();
 
    function();

    my::indirect_access();
    my::nested::function();

    println!("Hello Rust"); 
}
 
// rustc split.rs
// ./split

my/mod.rs 的内容:

复制代码
// 类似地,`mod inaccessible` 和 `mod nested` 将找到 `nested.rs` 和
// `inaccessible.rs` 文件,并在它们放到各自的模块中。
mod inaccessible;
pub mod nested;

pub fn function() {
    println!("called 'my::function()'");
}

fn private_function() {
    println!("called 'my::private_function()'");
}

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

my/nested.rs 的内容:

复制代码
pub fn function() {
    println!("called 'my::nested::function()'");
}

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

my/inaccessible.rs 的内容:

复制代码
#[allow(dead_code)]
pub fn public_function() {
    println!("called 'my::inaccessible::public_function()'");
}

我们看到代码仍然正常运行,就和前面的一样:

复制代码
PS F:\rustproject\rustbyexample\chapter10\example10_5> rustc split.rs
PS F:\rustproject\rustbyexample\chapter10\example10_5> ./split
called 'my::function()'
called 'function()'
called 'my::indirect_access()', that
> called 'my::private_function()'
called 'my::nested::function()'
Hello Rust
PS F:\rustproject\rustbyexample\chapter10\example10_5>
相关推荐
楼兰公子5 小时前
buildroot 在编译rust时裁剪平台类型数量的方法
开发语言·后端·rust
Rust研习社11 小时前
开源项目里的 deny.toml 是什么?
后端·rust·编程语言
铭毅天下17 小时前
当搜索引擎遇上 Rust——深度解读下一代实时搜索引擎 INFINI Pizza
开发语言·后端·搜索引擎·rust
咸甜适中17 小时前
rust语言学习笔记Trait之Default(默认值)
笔记·学习·rust
容智信息1 天前
AI Agent(智能体)的输出格式应该从 Markdown 转向 HTML吗?
前端·人工智能·rust·编辑器·html·prompt
Rust研习社2 天前
Rust Clippy 实用指南:写出更优雅、安全的 Rust 代码
后端·rust·编程语言
yangyongdehao302 天前
两天用AI+rust撸了一款本地批量去水印软件,30MB,效果能打
ai作画·rust
nudt_qxx2 天前
NVIDIA 正式开源cuda-oxide!Rust 编写 CUDA 内核新范式!
rust
小杍随笔3 天前
【Rust桌面革命:Tauri×Dioxus——架构对决、实战拆解与2026选型杀招】
开发语言·架构·rust