问题描述
我使用宏来生成一个模块,该模块定义了一个函数,该函数返回用户传入的类型:
printf
如果用户传入的是非公开类型:
macro_rules! generate_mod {
($name:ident: $type:ty = $e:expr) => {
mod $name {
use super::*;
static DATA: $type = $e;
pub fn get() -> &'static $type
{
return &DATA;
}
}
}
}
struct TestData(i32);
generate_mod!(foo: TestData = TestData(5));
这令人困惑,因为 rustc 抱怨的 private type `TestData` in public interface
方法与 get
具有相同的可见性。如果我将 TestData
定义中的 pub
更改为 get
,一切正常。
I reread the module documentation 并且我仍然不理解这种行为。 pub(crate)
应该只使 pub
可见一层(如文档所述,您需要一个公开链直到您要访问的项目),并且只要包含 {{1} } 不是 get
我不明白该类型是如何逃脱的。 get
使该函数对整个 crate 可见,这听起来在将事情公开方面应该更糟糕,所以我完全不明白为什么 rustc 更喜欢它。
解决方法
如果你展开宏调用,你会得到:
struct TestData(i32);
mod foo {
use super::*;
static DATA: TestData = TestData(5);
pub fn get() -> &'static TestData {
return &DATA;
}
}
由于此错误而无法编译:
error[E0446]: private type `TestData` in public interface
--> src/lib.rs:8:5
|
1 | struct TestData(i32);
| --------------------- `TestData` declared as private
...
8 | pub fn get() -> &'static TestData {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't leak private type
据说 TestData
是私有的,但它被 pub
函数泄露了。尽管 mod foo
不是 pub
,但它在 crate 中的任何位置都可见(因为 root 模块 默认为 pub(crate)
- 而 struct TestData
不是)。来自您喜欢自己的文档:
// 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 {...}
让我强调相关部分:
因为它在当前 crate 的根目录中是私有的,所以 crate 中的任何模块都可以访问该模块中任何公开可见的项目。
为了编译,你可以让你的结构体pub
:
pub struct TestData(i32);
或者为了保密,将您的函数设为 pub(super)
,以便只有来自 foo
的超级模块可以看到它:
#[derive(Debug)]
struct TestData(i32);
mod foo {
use super::*;
static DATA: TestData = TestData(5);
pub(super) fn get() -> &'static TestData {
return &DATA;
}
}
fn main() {
println!("{:?}",foo::get());
}
,
让我们看下面的例子:
pub mod container {
mod foo {
pub struct Bar;
pub(super) struct Baz;
struct Qux;
}
fn get_bar() -> foo::Bar {
foo::Bar
}
fn get_baz() -> foo::Baz {
foo::Baz
}
// error[E0603]: struct `Qux` is private
// fn get_qux() -> foo::Qux {
// foo::Qux
// }
pub fn pub_get_bar() -> foo::Bar {
foo::Bar
}
// error[E0446]: restricted type `Baz` in public interface
// pub fn pub_get_baz() -> foo::Baz {
// foo::Baz
// }
// error[E0603]: struct `Qux` is private
// pub fn pub_get_qux() -> foo::Qux {
// foo::Qux
// }
pub use foo::bar;
}
这里有两件事需要考虑:代码所在的位置,以及从哪里可以看到代码。在 Rust 中,可见性的工作方式有两种:
-
“私有”,或仅对指定路径内的代码可见。 “私有”代码的说明符是:
-
pub(self)
:对位于当前模块中的代码可见 -
pub(super)
:对位于父模块中的代码可见 -
pub(crate)
:对位于 crate 根目录中的代码可见 -
pub(in foo::bar)
:对位于给定路径内的代码可见,该路径必须是当前路径的祖先。1
正如我之前提到的,你总是可以访问你的祖先可以访问的任何东西,所以这实际上意味着一个项目被认为“位于”它的所有祖先中(例如
foo::bar::baz
也可以看到任何东西 {{ 1}} 或pub(in foo::bar)
)。 -
-
“公共”:这是通过普通的
pub(in foo)
指定的。公共项目在任何地方可见,只要其父项可见。 crate 根中的公共项目在外部可见。
(默认可见性是 pub
,而不是 pub(self)
,尽管它们在 crate 根的含义相同。正如您所见,“pub”有点用词不当,因为 {{ 1}} 实际上使事情变得私有,事实上这是明确地将某些事情私有化的唯一方法)
函数签名要求所有类型至少与函数本身一样可见。2
在上面的示例中,pub(crate)
的可见性默认为 pub(...)
,这实际上表示 container::foo
。在 pub(self)
(即 pub(in container)
)内的私有函数的签名中:
- 我们可以使用
container
,因为它是公开的,即使它的父级不是。3 - 我们可以使用
pub(in container)
,因为它的可见性是container::foo::Bar
,它至少与函数本身一样可见(在本例中,同样可见)。 - 我们不能使用
container::foo::Baz
,因为它的可见性是pub(in container)
,它比函数本身更不可见。事实上,我们甚至无法在函数体内访问它,因为我们不在container::foo::Qux
中。
对于 pub(in container::foo)
中的公共函数:
- 我们可以使用
container::foo
,因为它是公开的,即使它的父级不是。3 - 我们不能使用
container
,因为它是私有的,但这是一个公共函数。这就是您面临的问题。 - 我们不能像以前一样使用
container::foo::Bar
。
1。在 Rust 2018 中,路径必须是当前路径的祖先。以前,这在技术上可能是一个外部路径,甚至是一个外部 crate,这会使其成为半“公共”(私有给外部模块;奇怪,我知道,尽量避免它)。除此之外,私人物品只能在当前箱子中访问。
2.这有点奇怪,因为您可以对私有类型设置特定的泛型边界。
3.另一个不寻常的怪癖是公共项目总是被认为是公共的,即使它们似乎不能公开访问(至少通过直接路径到他们的声明)。但是,您始终可以“重新导出”它们:在示例中,container::foo::Baz
使 container::foo::Qux
通过 pub use foo::Bar
公开访问。这就是您的代码不起作用的原因。尽管如此,我的示例在没有该语句的情况下编译,并且在外部您可以完全使用 Bar
返回的 container::Bar
的任何实例,即使您无法访问类型本身(并且 rustdoc 甚至不会生成文档为了它)。由于这很奇怪,我强烈建议不要将公共项目放入私有模块中,除非您确保重新导出所有内容。