可见性和私有性
Syntax
Visibility →
pub
| pub ( crate )
| pub ( self )
| pub ( super )
| pub ( in SimplePath )
这两个术语(Visibility 和 Privacy)经常互换使用,它们试图传达的是“这个项可以在这个位置使用吗?“这个问题的答案。
Rust 的名称解析在命名空间的全局层次结构上操作。层次结构中的每一层都可以被认为是某个项。这些项是上面提到的那些,但也包括外部 crate。声明或定义一个新模块可以被认为是将一个新树插入到定义位置的层次结构中。
为了控制接口是否可以跨模块使用,Rust 检查每个项的使用以查看是否应该允许。这就是生成隐私警告的地方,或者“您使用了另一个模块的私有项,并且不被允许。“
默认情况下,一切都是私有的,有两个例外:pub Trait 中的关联项默认是公开的;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 允许在两种情况下进行项访问:
- 如果一个项是公开的,那么如果您可以从
m访问该项的所有祖先模块,则可以从某个模块m外部访问它。您也可以通过重导出来命名该项。见下文。 - 如果一个项是私有的,它可能被当前模块及其后代访问。
这两种情况对于创建公开 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 Edition differences
从 2018 版本开始,
pub(in path)的路径必须以crate、self或super开头。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() }
Note
此语法仅向项的可见性添加另一个限制。它不保证该项在指定作用域的所有部分都可见。要访问一个项,其所有父项直到当前作用域都必须仍然可见。
重导出和可见性
Rust 允许通过 pub use 指令公开重导出项。因为这是一个公开指令,这允许根据上述规则在当前模块中使用该项。它本质上允许对重导出项的公开访问。例如,此程序是有效的:
pub use self::implementation::api;
mod implementation {
pub mod api {
pub fn f() {}
}
}
fn main() {}
这意味着任何引用 implementation::api::f 的外部 crate 都会收到隐私违规,而路径 api::f 将被允许。
当重导出私有项时,可以认为它允许“隐私链“通过重导出短路,而不是像通常那样通过命名空间层次结构传递。