当前位置: 首页 > article >正文

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

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

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

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

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

    front_of_house::hosting::add_to_waitlist();
    

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

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

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

crate
 └── front_of_house
     ├── hosting
     │   └── add_to_waitlist
     └── serving
         ├── take_order
         ├── serve_order
         └── take_payment

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

  • 使用绝对路径
    pub fn eat_at_restaurant() {
        // 从 crate 根开始,使用绝对路径调用 add_to_waitlist
        crate::front_of_house::hosting::add_to_waitlist();
    }
    
  • 使用相对路径
    pub fn eat_at_restaurant() {
        // 从当前模块开始,直接用模块名调用
        front_of_house::hosting::add_to_waitlist();
    }
    

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

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

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

error[E0603]: module `hosting` is private

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

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

// 在 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

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

    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 后,其所有变体默认都为公共的:

    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!


http://www.kler.cn/a/533186.html

相关文章:

  • 智慧园区系统集成解决方案提升管理效率与智能化水平的新探索
  • python算法和数据结构刷题[3]:哈希表、滑动窗口、双指针、回溯算法、贪心算法
  • 【教程】禁止网页右键和打开调试页面
  • 996引擎-怪物:Lua 刷怪+清怪+自动拾取
  • kamailio的kamctl的使用
  • selenium记录Spiderbuf例题C03
  • mysql 学习8 函数,字符串函数,数值函数,日期函数,流程函数
  • 18.[前端开发]Day18-王者荣耀项目实战(一)
  • Scheme语言的正则表达式
  • 传输层协议——TCP协议
  • LeetCode 0922.按奇偶排序数组 II:O(1)空间复杂度-一次遍历双指针
  • 青少年编程与数学 02-008 Pyhon语言编程基础 19课题、外部模块
  • 【数据采集】基于Selenium采集豆瓣电影Top250的详细数据
  • 【Day29 LeetCode】动态规划DP
  • Rust中变量【引用】与【借用】规则
  • Markdown转换器中间件
  • AI协助探索AI新构型自动化创新的技术实现
  • 【现代深度学习技术】深度学习计算 | 延后初始化自定义层
  • 决策规划概述
  • C# 数组、索引器与集合介绍
  • 面向智慧农业的物联网监测系统设计(论文+源码+实物)
  • [LeetCode] 栈与队列 I — 232#用栈实现队列 | 225#用队列实现栈 | 20#有效的括号 | 1047#删除字符串中的所有相邻重复项
  • ES6-rest参数、数组扩展中的扩展运算符
  • CPU、MCU、MPU、SOC、DSP、ECU、GPU、FPGA傻傻分不清楚?一文讲清它们的区别
  • (十一)机器人系统的仿真——建造机器人模型
  • 4. k8s二进制集群之ETCD集群证书生成