可见性和隐私性

语法
可见性 :
      pub
   | pub ( crate )
   | pub ( self )
   | pub ( super )
   | pub ( in SimplePath )

这两个术语经常互换使用,它们试图传达的是对问题 “这个条目可以在这个位置使用吗?” 的回答。

Rust 的名称解析在一个全局的命名空间层级结构上操作。层级结构中的每一层都可以被认为是某个条目。这些条目是上面提到的那些,但也包括外部 crate。声明或定义一个新的模块可以被认为是在层级结构中定义的位置插入一个新的树。

为了控制接口是否可以在模块之间使用,Rust 检查每个条目的使用情况,以查看是否应该允许使用。这就是生成隐私警告的地方,或者换句话说,“你使用了另一个模块的私有条目,但不被允许。”

默认情况下,一切都是私有的,但有两个例外:pub Trait 中的关联条目默认是公共的;pub enum 中的枚举变体也默认是公共的。当一个条目被声明为 pub 时,它可以被认为是可供外部世界访问的。例如

fn main() {}
// Declare a private struct
struct Foo;

// Declare a public struct with a private field
pub struct Bar {
    field: i32,
}

// Declare a public enum with two public variants
pub enum State {
    PubliclyAccessibleState,
    PubliclyAccessibleState2,
}

有了条目是公共的或私有的概念,Rust 允许在两种情况下访问条目

  1. 如果一个条目是公共的,那么如果你可以从模块 m 访问该条目的所有祖先模块,则可以从模块 m 外部访问它。你也可以通过重新导出命名该条目。见下文。
  2. 如果一个条目是私有的,它可以被当前模块及其后代访问。

这两种情况在创建模块层级结构以公开公共 API 同时隐藏内部实现细节方面非常强大。为了帮助解释,这里有一些用例以及它们将包含的内容

  • 库开发者需要向链接到其库的 crate 公开功能。作为第一种情况的结果,这意味着任何可以在外部使用的东西都必须从根到目标条目都是 pub 的。链中的任何私有条目都将禁止外部访问。

  • 一个 crate 需要一个全局可用的“助手模块”供自身使用,但它不想将助手模块作为公共 API 公开。为了实现这一点,crate 层级结构的根将有一个私有模块,然后在内部有一个“公共 API”。因为整个 crate 是根的后代,所以整个本地 crate 可以通过第二种情况访问这个私有模块。

  • 当为模块编写单元测试时,通常的做法是将要测试的模块的直接子模块命名为 mod test。这个模块可以通过第二种情况访问父模块的任何条目,这意味着也可以从子模块无缝地测试内部实现细节。

在第二种情况中,它提到私有条目“可以被访问” 当前模块及其后代,但是访问条目的确切含义取决于条目是什么。

例如,访问一个模块意味着查看其内部(以导入更多条目)。另一方面,访问一个函数意味着调用它。此外,路径表达式和导入语句被认为是在访问条目的意义上,只有当目标在当前可见性范围内时,导入/表达式才是有效的。

这是一个程序示例,它例证了上面概述的三种情况

// This module is private, meaning that no external crate can access this
// module. Because it is private at the root of this current crate, however, any
// module in the crate may access any publicly visible item in this module.
mod crate_helper_module {

    // This function can be used by anything in the current crate
    pub fn crate_helper() {}

    // This function *cannot* be used by anything else in the crate. It is not
    // publicly visible outside of the `crate_helper_module`, so only this
    // current module and its descendants may access it.
    fn implementation_detail() {}
}

// This function is "public to the root" meaning that it's available to external
// crates linking against this one.
pub fn public_api() {}

// Similarly to 'public_api', this module is public so external crates may look
// inside of it.
pub mod submodule {
    use crate::crate_helper_module;

    pub fn my_method() {
        // Any item in the local crate may invoke the helper module's public
        // interface through a combination of the two rules above.
        crate_helper_module::crate_helper();
    }

    // This function is hidden to any module which is not a descendant of
    // `submodule`
    fn my_implementation() {}

    #[cfg(test)]
    mod test {

        #[test]
        fn test_my_implementation() {
            // Because this module is a descendant of `submodule`, it's allowed
            // to access private items inside of `submodule` without a privacy
            // violation.
            super::my_implementation();
        }
    }
}

fn main() {}

为了使 Rust 程序通过隐私检查,鉴于上述两个规则,所有路径都必须是有效的访问。这包括所有 use 语句、表达式、类型等。

pub(in path)pub(crate)pub(super)pub(self)

除了公共和私有之外,Rust 允许用户声明一个条目仅在给定范围内可见。pub 限制的规则如下

  • pub(in path) 使条目在提供的 path 中可见。path 必须是一个简单路径,它解析为声明其可见性的条目的祖先模块。path 中的每个标识符都必须直接引用一个模块(而不是由 use 语句引入的名称)。
  • pub(crate) 使条目在当前 crate 中可见。
  • pub(super) 使条目对父模块可见。这等同于 pub(in super)
  • pub(self) 使条目对当前模块可见。这等同于 pub(in self) 或根本不使用 pub

版本差异:从 2018 版本开始,pub(in path) 的路径必须以 crateselfsuper 开头。2015 版本也可以使用以 :: 或 crate 根模块开头的路径。

这是一个例子

pub mod outer_mod {
    pub mod inner_mod {
        // This function is visible within `outer_mod`
        pub(in crate::outer_mod) fn outer_mod_visible_fn() {}
        // Same as above, this is only valid in the 2015 edition.
        pub(in outer_mod) fn outer_mod_visible_fn_2015() {}

        // This function is visible to the entire crate
        pub(crate) fn crate_visible_fn() {}

        // This function is visible within `outer_mod`
        pub(super) fn super_mod_visible_fn() {
            // This function is visible since we're in the same `mod`
            inner_mod_visible_fn();
        }

        // This function is visible only within `inner_mod`,
        // which is the same as leaving it private.
        pub(self) fn inner_mod_visible_fn() {}
    }
    pub fn foo() {
        inner_mod::outer_mod_visible_fn();
        inner_mod::crate_visible_fn();
        inner_mod::super_mod_visible_fn();

        // This function is no longer visible since we're outside of `inner_mod`
        // Error! `inner_mod_visible_fn` is private
        //inner_mod::inner_mod_visible_fn();
    }
}

fn bar() {
    // This function is still visible since we're in the same crate
    outer_mod::inner_mod::crate_visible_fn();

    // This function is no longer visible since we're outside of `outer_mod`
    // Error! `super_mod_visible_fn` is private
    //outer_mod::inner_mod::super_mod_visible_fn();

    // This function is no longer visible since we're outside of `outer_mod`
    // Error! `outer_mod_visible_fn` is private
    //outer_mod::inner_mod::outer_mod_visible_fn();

    outer_mod::foo();
}

fn main() { bar() }

注意: 此语法仅向条目的可见性添加了另一个限制。它不保证条目在指定范围的所有部分都可见。要访问一个条目,其直到当前范围的所有父条目也必须是可见的。

重新导出和可见性

Rust 允许通过 pub use 指令公开重新导出条目。因为这是一个公共指令,所以这允许通过上述规则在当前模块中使用该条目。它本质上允许公共访问重新导出的条目。例如,这个程序是有效的

pub use self::implementation::api;

mod implementation {
    pub mod api {
        pub fn f() {}
    }
}

fn main() {}

这意味着任何引用 implementation::api::f 的外部 crate 都会收到隐私违规,而路径 api::f 将被允许。

当重新导出私有条目时,可以认为是通过重新导出而不是像通常那样通过命名空间层级结构来“短路”隐私链。