作为一名大学生,你可能已经写过一些 Rust 代码,用过 pub 关键字。但你是否曾困惑:为什么有些函数在模块外无法调用?为什么有些结构体字段无法直接访问?这背后其实是 Rust 精心设计的一套可见性规则,它就像一套“家族隐私法则”,用于管理代码的访问权限。

一、从一个简单的“家族”类比开始

想象一下,你的大学里有一个社团(Crate)。这个社团里有很多部门(Module),比如“技术部”、“宣传部”等。

  • Crate (社团):是你项目的最外层,一个独立的编译单元。可以是一个二进制程序(src/main.rs)或一个库(src/lib.rs)。

  • Module (部门):用于组织代码、控制可见性(隐私)的命名空间。

在这个社团里,每个部门(模块)都有自己的“家规”,规定了哪些家当(函数、结构体、变量等)是:

  1. 完全公开的:任何人都可以来看、来用。

  2. 部门内部使用的:只有本部门的成员才能使用。

  3. 对友邻部门开放的:只有社团内部的其他部门可以使用,外人不行。

Rust 的可见性系统,就是在帮我们定义这些“家规”。

二、核心关键字:pub 的不同修饰符

pub 关键字就是用来声明“这个东西是公开的”。但公开给谁?这里有不同的级别:

可见性级别 关键字 类比解释
私有 (无,默认) 部门内部的秘密文件,只有本部门的人能看。
完全公开 pub 社团的海报,贴在校内公告栏,校内校外的人都能看。
在父模块中可见 pub(super) 给上级领导(父部门)看的汇报材料。
在整个Crate内可见 pub(crate) 社团内部共享的文档,所有部门都能看,但不会发给校外的人。
在指定路径可见 pub(in path::to::module) 指定只给某个“友邻部门”看的资料。

默认情况(私有):在 Rust 中,所有项(函数、结构体、枚举、常量等)默认都是私有的。这体现了 Rust 的安全哲学:除非你明确允许,否则一切都是封闭的。

三、图示:一个代码“社团”的结构

让我们来看一个具体的代码例子和它的结构图。

// src/lib.rs - 我们的“社团”总部

pub mod department_a { // 部门A
    pub fn public_function() { // 完全公开的函数
        println!("Anyone can call me!");
    }

    pub(crate) fn internal_docs() { // 社团内部使用的函数
        println!("For crate eyes only.");
    }

    fn private_function() { // 部门私有的函数
        println!("Top secret of department A!");
    }

    pub mod secret_team { // 部门A下的一个秘密小组
        use super::private_function; // 可以访问上级的私有项

        pub fn do_secret_work() {
            println!("Secret team is working...");
            private_function(); // 合法!小组可以调用部门的私有函数
        }
    }
}

pub mod department_b { // 部门B
    use crate::department_a;

    pub fn try_access() {
        department_a::public_function(); // 合法!完全公开
        department_a::internal_docs();   // 合法!同属一个crate

        // department_a::private_function(); // 错误!无法访问其他部门的私有项
        // department_a::secret_team::do_secret_work(); // 合法!因为do_secret_work是pub的
    }
}

// 来自“校外”的访问(比如在 main.rs 中)
use my_crate::department_a;

fn main() {
    department_a::public_function(); // 合法!
    // department_a::internal_docs(); // 错误!校外的人看不到内部文档
}

现在,我们用一张图来直观展示上面的访问关系:

text

+--------------------------------------------------------------------+
|                        Crate: my_crate (社团)                      |
|                                                                    |
|  +---------------------------+    +---------------------------+   |
|  |   Module: department_a    |    |   Module: department_b    |   |
|  |                           |    |                           |   |
|  |  +---------------------+  |    |  +---------------------+  |   |
|  |  | pub fn public_fn    |<------|--| (可以访问)          |  |   |
|  |  | (对外完全公开)       |  |    |  |                     |  |   |
|  |  +---------------------+  |    |  +---------------------+  |   |
|  |                           |    |                           |   |
|  |  +---------------------+  |    |  +---------------------+  |   |
|  |  | pub(crate) internal |<-------|--| (可以访问)          |  |   |
|  |  | (仅社团内部可见)     |  |    |  |                     |  |   |
|  |  +---------------------+  |    |  +---------------------+  |   |
|  |                           |    |                           |   |
|  |  +---------------------+  |    |  +---------------------+  |   |
|  |  | fn private_fn       |  |    |  | (无法访问!)          |  |   |
|  |  | (部门A私有)         |  |    |  |                     |  |   |
|  |  +---------------------+  |    |  +---------------------+  |   |
|  |          ^                |    |                           |   |
|  |          | (super可以访问) |    +---------------------------+   |
|  |  +---------------------+  |                                    |
|  |  | mod secret_team     |  |                                    |
|  |  |                     |  |    +---------------------------+   |
|  |  | +-----------------+ |  |    |  External: src/main.rs    |   |
|  |  | | (可以调用       | |  |    |  (校外)                   |   |
|  |  | | private_fn)     | |  |    |                           |   |
|  |  | +-----------------+ |  |    |  +---------------------+  |   |
|  |  +---------------------+  |    |  | 只能访问 public_fn  |--|------+
|  +---------------------------+    |  | (无法访问internal!) |  |   |
|                                   |  +---------------------+  |   |
+--------------------------------------------------------------------+

图解说明:

  • 实线箭头:表示允许的访问。

  • 虚线箭头并带“叉”:表示被规则禁止的访问。

  • department_b 可以访问 department_a 的 public 和 pub(crate) 项,因为它们同属一个 Crate。

  • secret_team 可以访问其父模块 department_a 的私有项,这是 super 路径的威力。

  • 外部的 main.rs 只能访问被标记为 pub 的项,pub(crate) 的项对它来说是隐藏的。

四、结构体与枚举的可见性

可见性规则同样适用于复合类型,但有一点小变化。

1. 结构体(Struct)

结构体本身的可见性和其字段的可见性是分开的

pub mod department_a {
    // 这个结构体对外部是公开的
    pub struct OpenHouse {
        pub address: String, // 地址也是公开的
        pub(crate) internal_phone: String, // 内部电话只有crate内能访问
        secret_code: u32, // 密码是绝对私有的,外界甚至无法访问这个字段
    }

    impl OpenHouse {
        // 一个公共的构造函数,是创建OpenHouse的唯一方式
        pub fn new(addr: String, phone: String, code: u32) -> Self {
            OpenHouse {
                address: addr,
                internal_phone: phone,
                secret_code: code,
            }
        }

        // 一个公共的方法,可以间接读取私有字段
        pub fn check_code(&self, code: u32) -> bool {
            self.secret_code == code
        }
    }
}

// 在外部使用
use my_crate::department_a::OpenHouse;

fn main() {
    let house = OpenHouse::new("Rust Ave".to_string(), "123".to_string(), 42);
    println!("Address: {}", house.address); // 合法
    // println!("Phone: {}", house.internal_phone); // 错误!外部无法访问
    // println!("Code: {}", house.secret_code); // 错误!字段私有

    house.check_code(50); // 合法,通过公共接口与私有字段交互
}

要点:即使结构体是 pub 的,其字段也默认是私有的。这实现了封装,你可以精确控制哪些数据可以直接修改,哪些必须通过方法。

2. 枚举(Enum)

枚举的可见性规则更简单:如果枚举是 pub 的,那么它的所有变体也都是公开的

pub mod department_a {
    pub enum PublicEvent { // 公开的枚举
        OpenSeminar,       // 所有变体自动公开
        Workshop,          // 所有变体自动公开
        InternalMeeting,   // 所有变体自动公开
    }
}

这是因为枚举的变体是其类型的组成部分,将它们全部公开通常更有用。

五、为什么要这样设计?给大学生的启示

  1. 契约与安全:可见性规则在你(代码作者)和用户(其他程序员,包括未来的你)之间建立了一份清晰的“契约”。它明确指出了哪些功能是稳定的、可供外部使用的 API,哪些是可能变化的内部实现细节。这避免了用户误用内部接口,从而在你修改内部代码时,他们的代码不会崩溃。

  2. 高内聚,低耦合:鼓励你将相关的数据和行为封装在模块内。模块内部可以自由地修改和重构,只要不改变其公开的接口,就不会影响到外部代码。这是构建大型、可维护软件系统的基石。

  3. 清晰的抽象边界:通过强制使用 pub 来暴露接口,Rust 促使你思考:“这个函数/结构体真的需要被外界使用吗?” 这有助于设计出更清晰、更抽象的 API。

总结

可以把 Rust 的可见性系统想象成一个精密的权限管理系统:

  • 默认私有:保护你的代码,防止意外耦合。

  • pub:打开大门,完全公开。

  • pub(crate):一个非常实用的中间地带,用于创建crate 内部的 API,对外隐藏实现细节。

  • pub(super) 和 pub(in path):用于更精细的作用域控制,在复杂的模块树中非常有用。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐