在 Rust 库中仅公开通用私有类型的具体变体
Exposing only a concrete variant of a generic private type in a Rust library
我有一个 Rust 库 crate,其代码结构如下:
pub struct Foo<X> {
x: X,
}
pub fn foo() -> Foo<u32> {
// ...
}
// + private functions
特别是,虽然库在内部使用 Foo
的不同变体(例如 Foo<u8>
、Foo<u32>
),但 Foo
仅出现在库的 public API 作为 Foo<u32>
.
像我目前所做的那样公开通用 Foo
会使库的 public API 及其文档不必要地复杂:用户永远不会从库中获得 Foo
那不是 Foo<u32>
。因此,我想以某种方式只公开和 publicly 记录 Foo<u32>
(最好使用不同的非通用名称,例如 Bar
)并使 Foo
私有。
我试过使用类型别名 (type Bar = Foo<u32>
),但它们似乎会自动扩展 cargo doc
(而且它们似乎也没有单独的可见性)。
我可能会复制 Foo<X>
的定义并将其命名为 Bar
,然后为 Bar
实现类似 From<Foo<u32>>
的东西。但是,我对 Foo<X>
的实际定义相当复杂,所以我想避免这种情况。
还有其他方法可以实现我的目标吗?
您可以从父模块公开类型,如下所示:
mod prelude {
mod foo_mod {
pub struct Foo<X> {
x: X,
}
impl Foo<u32> {
pub fn foo() -> u32 {
32
}
}
impl Foo<u8> {
pub fn foo() -> u8 {
8
}
}
}
pub type FooBar = foo_mod::Foo<u32>;
}
fn main() {
use prelude::FooBar; // we can use this
use prelude::foo_mod::Foo; // we cannot use this
}
我反对 Netwave 技巧,它欺骗了 public 的规则和 Rust 的私有,在我看来这样的代码不应该编译。这将 Foo
暴露给用户,因此实际上 Foo
完全是 public,对 Foo
的任何更改都是 重大更改。
根据您的问题描述:
In particular, while the lib uses different variants of Foo internally (e.g. Foo, Foo), Foo only occurs in the lib's public API as Foo.
解决方案是制作一个真正的包装器:
pub struct FooBar {
foo: Foo<u32>,
}
impl FooBar {
pub fn some_pub_fct();
}
这是你应该做的,因为正如你所说,用户不需要泛型,所以你想要的是隐藏用户的实现细节。这比泄露私人项目的 netwave 技巧要好得多。这种方式很明显,更改 FooBar
是一项重大更改,因为它应该是 public 项目。您可以随意更改 Foo
。用户赢,你赢。
我有一个 Rust 库 crate,其代码结构如下:
pub struct Foo<X> {
x: X,
}
pub fn foo() -> Foo<u32> {
// ...
}
// + private functions
特别是,虽然库在内部使用 Foo
的不同变体(例如 Foo<u8>
、Foo<u32>
),但 Foo
仅出现在库的 public API 作为 Foo<u32>
.
像我目前所做的那样公开通用 Foo
会使库的 public API 及其文档不必要地复杂:用户永远不会从库中获得 Foo
那不是 Foo<u32>
。因此,我想以某种方式只公开和 publicly 记录 Foo<u32>
(最好使用不同的非通用名称,例如 Bar
)并使 Foo
私有。
我试过使用类型别名 (type Bar = Foo<u32>
),但它们似乎会自动扩展 cargo doc
(而且它们似乎也没有单独的可见性)。
我可能会复制 Foo<X>
的定义并将其命名为 Bar
,然后为 Bar
实现类似 From<Foo<u32>>
的东西。但是,我对 Foo<X>
的实际定义相当复杂,所以我想避免这种情况。
还有其他方法可以实现我的目标吗?
您可以从父模块公开类型,如下所示:
mod prelude {
mod foo_mod {
pub struct Foo<X> {
x: X,
}
impl Foo<u32> {
pub fn foo() -> u32 {
32
}
}
impl Foo<u8> {
pub fn foo() -> u8 {
8
}
}
}
pub type FooBar = foo_mod::Foo<u32>;
}
fn main() {
use prelude::FooBar; // we can use this
use prelude::foo_mod::Foo; // we cannot use this
}
我反对 Netwave 技巧,它欺骗了 public 的规则和 Rust 的私有,在我看来这样的代码不应该编译。这将 Foo
暴露给用户,因此实际上 Foo
完全是 public,对 Foo
的任何更改都是 重大更改。
根据您的问题描述:
In particular, while the lib uses different variants of Foo internally (e.g. Foo, Foo), Foo only occurs in the lib's public API as Foo.
解决方案是制作一个真正的包装器:
pub struct FooBar {
foo: Foo<u32>,
}
impl FooBar {
pub fn some_pub_fct();
}
这是你应该做的,因为正如你所说,用户不需要泛型,所以你想要的是隐藏用户的实现细节。这比泄露私人项目的 netwave 技巧要好得多。这种方式很明显,更改 FooBar
是一项重大更改,因为它应该是 public 项目。您可以随意更改 Foo
。用户赢,你赢。