可见性和私有性

语法

可见性 :

      pub

   | pub ( crate )

   | pub ( self )

   | pub ( super )

   | pub ( in 简单路径 )

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

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

为了控制接口是否可以在模块之间使用,Rust 会检查每个项目的每次使用,以查看是否应该允许这样做。这就是生成隐私警告的地方,或者说是“您使用了另一个模块的私有项目,并且不允许这样做”。

默认情况下,所有内容都是*私有的*,有两个例外:pub 特征中的关联项默认是公共的;pub 枚举中的枚举变体默认也是公共的。当一个项目被声明为 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,同时隐藏内部实现细节。为了帮助解释,这里有一些用例以及它们将需要什么

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

  • 一个包需要一个全局可用的“帮助模块”,但它不想将帮助模块公开为公共 API。为了实现这一点,包层次结构的根目录将有一个私有模块,该模块在内部有一个“公共 API”。因为整个包都是根目录的后代,所以整个本地包可以通过第二种情况访问这个私有模块。

  • 在为模块编写单元测试时,通常的做法是在要测试的模块的直接子模块中命名为 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 程序通过隐私检查,所有路径都必须是有效的访问,并给出上述两条规则。这包括所有使用语句、表达式、类型等。

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

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

  • pub(in path) 使项目在提供的 path 中可见。path 必须是正在声明其可见性的项目的祖先模块。
  • pub(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 将被允许。

重新导出私有项目时,可以将其视为允许“隐私链”通过重新导出进行短路,而不是像通常那样通过命名空间层次结构传递。