深入理解 Rust 模块中的路径与公开性:绝对路径、相对路径和 `pub` 的应用

1. 路径的两种形式:绝对路径与相对路径

在 Rust 中,路径类似于文件系统中的目录路径,用来告诉编译器去哪里查找某个项。路径主要有两种形式:

  • 绝对路径

    绝对路径从 crate 的根开始。对于当前 crate 的代码,绝对路径以关键字 crate 开头;对于外部 crate,则以该 crate 的名称开始。
    示例 :假设我们要调用 add_to_waitlist 函数,其完整的绝对路径可能是:

    rust 复制代码
    crate::front_of_house::hosting::add_to_waitlist();
  • 相对路径

    相对路径则是从当前模块出发,利用 selfsuper 或直接用模块名称来指定路径。例如,如果当前模块与 front_of_house 同级,可以直接这样调用:

    rust 复制代码
    front_of_house::hosting::add_to_waitlist();

选择使用哪种路径主要取决于代码重构时模块之间可能的移动。如果函数与其被调用项经常会一起移动,那么使用相对路径可能更方便;否则,绝对路径则提供了更稳定的引用。

2. 通过路径调用函数:一个示例

假设我们有如下模块结构(部分代码摘自 Listing 7-1):

text 复制代码
crate
 └── front_of_house
     ├── hosting
     │   └── add_to_waitlist
     └── serving
         ├── take_order
         ├── serve_order
         └── take_payment

我们希望在同一个 crate 根中定义一个名为 eat_at_restaurant 的函数来调用 add_to_waitlist 函数。可以使用两种不同的路径来实现这一目标:

  • 使用绝对路径

    rust 复制代码
    pub fn eat_at_restaurant() {
        // 从 crate 根开始,使用绝对路径调用 add_to_waitlist
        crate::front_of_house::hosting::add_to_waitlist();
    }
  • 使用相对路径

    rust 复制代码
    pub fn eat_at_restaurant() {
        // 从当前模块开始,直接用模块名调用
        front_of_house::hosting::add_to_waitlist();
    }

无论选择哪种方式,关键在于准确表达代码在模块树中的位置。

3. 模块私有性问题:编译错误的根源

在实际编译过程中,可能会遇到类似下面的错误:

console 复制代码
error[E0603]: module `hosting` is private

这表明即使路径写得正确,由于 Rust 默认将模块中的所有项设置为私有,外部模块无法直接访问 hosting 模块中的内容。为了让父模块中的 eat_at_restaurant 能够调用 add_to_waitlist,不仅需要使 hosting 模块变为公共(使用 pub mod hosting;),还需要将 add_to_waitlist 函数本身声明为 pub

例如,假设我们的代码如下:

rust 复制代码
// 在 src/lib.rs 中
mod front_of_house {
    // 将 hosting 模块标记为 pub,让外部可以访问该模块
    pub mod hosting {
        // add_to_waitlist 函数默认是私有的,需要加 pub
        pub fn add_to_waitlist() {
            println!("Adding to waitlist");
        }
    }
}

pub fn eat_at_restaurant() {
    // 两种路径均可使用,因为模块与函数都已经公开
    crate::front_of_house::hosting::add_to_waitlist();
    // 或者:
    front_of_house::hosting::add_to_waitlist();
}

只有同时使用 pub 将模块和函数公开,调用代码才能通过编译。

4. 使用 super 构造相对路径

有时我们需要引用父模块中的项,此时可以使用 super 关键字,其效果类似于文件路径中的 ..。例如,在 back_of_house 模块中,假设有一个函数 fix_incorrect_order 需要调用父模块中的 deliver_order

rust 复制代码
mod back_of_house {
    pub fn fix_incorrect_order() {
        // 使用 super 关键字引用父模块中的 deliver_order
        super::deliver_order();
    }
}

fn deliver_order() {
    println!("Order delivered!");
}

这种写法不仅直观,而且在模块重构时可以减少修改路径的地方,因为父子模块之间的相对关系保持不变。

5. 让 Struct 和 Enum 成为公共接口

除了函数和模块,结构体(structs)和枚举(enums)也是模块系统中常见的构建块。不过需要注意的是:

  • 结构体(structs)

    使用 pub 标记结构体可以使其整体公开,但结构体的字段默认仍然是私有的。如果希望外部代码能够访问或修改某些字段,需要在字段前也加上 pub

    rust 复制代码
    pub struct Breakfast {
        pub toast: String,         // 公开字段
        seasonal_fruit: String,    // 私有字段
    }
    
    impl Breakfast {
        // 提供公共关联函数来构造实例
        pub fn summer(toast: &str) -> Breakfast {
            Breakfast {
                toast: toast.to_string(),
                seasonal_fruit: String::from("peach"),
            }
        }
    }

    这样,外部代码可以访问 toast 字段,但无法直接修改 seasonal_fruit

  • 枚举(enums)

    与结构体不同,枚举在声明为 pub 后,其所有变体默认都为公共的:

    rust 复制代码
    pub enum Appetizer {
        Soup,
        Salad,
    }

    外部代码可以直接使用 Appetizer::SoupAppetizer::Salad,而无需单独标记每个变体为公共。

6. 小结与最佳实践

通过本文,我们了解了以下关键概念:

  • 路径的形式 :绝对路径从 crate 或外部 crate 开始,而相对路径则以当前模块为起点,必要时可使用 selfsuper 等关键字。
  • 模块私有性 :Rust 默认将所有项设为私有,使用 pub 可以有选择地暴露接口,从而形成稳定的公共 API。
  • 最佳实践
    • 在设计公共 API 时,将模块树组织在 src/lib.rs 中,确保只有明确标记为 pub 的项可供外部使用。
    • 在项目中使用绝对路径可以更好地分离代码定义和使用,减少因代码重构导致的路径修改。
    • 利用 super 构造相对路径,使得父子模块之间的引用更为灵活和稳健。

通过这些机制,Rust 为开发者提供了一套强大而灵活的模块系统,既能保持内部实现的封装,又能方便外部代码调用需要暴露的接口。

希望这篇博客能帮助你更深入地理解 Rust 模块中的路径引用与公开性设置,从而编写出更清晰、健壮的代码。Happy coding!

相关推荐
呼Lu噜25 分钟前
WPF-遵循MVVM框架创建图表的显示【保姆级】
前端·后端·wpf
bing_15830 分钟前
为什么选择 Spring Boot? 它是如何简化单个微服务的创建、配置和部署的?
spring boot·后端·微服务
小臭希32 分钟前
Java——琐碎知识点一
java·开发语言
学c真好玩43 分钟前
Django创建的应用目录详细解释以及如何操作数据库自动创建表
后端·python·django
Asthenia041243 分钟前
GenericObjectPool——重用你的对象
后端
Piper蛋窝1 小时前
Go 1.18 相比 Go 1.17 有哪些值得注意的改动?
后端
excel1 小时前
招幕技术人员
前端·javascript·后端
盖世英雄酱581361 小时前
什么是MCP
后端·程序员
淋一遍下雨天1 小时前
Spark Streaming核心编程总结(四)
java·开发语言·数据库
小白学大数据2 小时前
如何避免爬虫因Cookie过期导致登录失效
开发语言·爬虫·python·scrapy