在 Rust 库中仅公开泛型私有类型的具体变体
Posted
技术标签:
【中文标题】在 Rust 库中仅公开泛型私有类型的具体变体【英文标题】:Exposing only a concrete variant of a generic private type in a Rust library 【发布时间】:2022-01-03 05:56:57 【问题描述】:我有一个 Rust 库 crate,其代码结构如下:
pub struct Foo<X>
x: X,
pub fn foo() -> Foo<u32>
// ...
// + private functions
特别是,虽然 lib 在内部使用 Foo
的不同变体(例如 Foo<u8>
、Foo<u32>
),但 Foo
仅作为 Foo<u32>
出现在 lib 的公共 API 中。
像我目前所做的那样公开通用Foo
会使lib 的公共API 及其文档变得不必要地复杂:用户永远不会从不是Foo<u32>
的库中获得Foo
。因此,我想以某种方式只公开和公开记录Foo<u32>
(最好使用不同的非通用名称,例如Bar
)并将Foo
设为私有。
我尝试过使用类型别名 (type Bar = Foo<u32>
),但似乎它们会自动扩展为 cargo doc
(而且它们似乎也没有单独的可见性)。
我可能会复制Foo<X>
的定义并将其命名为Bar
,然后为Bar
实现类似From<Foo<u32>>
的东西。但是,我对Foo<X>
的实际定义相当复杂,所以我想避免这种情况。
还有其他方法可以实现我的目标吗?
【问题讨论】:
将模块公开给 super,然后从 super 公开具体类型作为别名。 @Netwave 这听起来很有趣,但我真的不知道该怎么做。您能否在答案中发布一个简短的示例? 我用这个想法添加了一个答案,它实际上并没有与pub(super)
一起使用,但背后的主要想法确实有效。
【参考方案1】:
您可以按如下方式从父模块公开类型:
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
Playground
【讨论】:
谢谢,太好了。我唯一的问题是缺少cargo doc
为FooBar
生成的文档。但是,这可能是一个不同的问题,所以我发布了a separate question。【参考方案2】:
我不建议 Netwave 把戏,它欺骗了 Rust 的公私规则,我认为这样的代码不应该编译。这会将Foo
暴露给用户,因此实际上Foo
是完全公开的,对Foo
的任何更改都是重大更改。
根据您的问题描述:
特别是,虽然 lib 在内部使用不同的 Foo 变体(例如 Foo、Foo),但 Foo 仅作为 Foo 出现在 lib 的公共 API 中。
解决方案是做一个真正的包装器:
pub struct FooBar
foo: Foo<u32>,
impl FooBar
pub fn some_pub_fct();
正如你所说,这是你应该做的,因为用户不需要泛型,所以你想要的是隐藏用户的实现细节。这比泄露私人项目的 netwave 技巧要好得多。这种方式很清楚,更改FooBar
是一项重大更改,因为它被认为是公共项目。您可以随意更改Foo
。用户赢了,你就赢了。
【讨论】:
您的示例与我的示例有很大的不同:您通过将Foo<u32>
设为FooBar
中的私有字段,将Foo<u32>
隐藏在公共API 中。但是,我的 API 的用户需要访问Foo<u32>
(我的公共foo
函数返回Foo<u32>
的实例)。在我的实际用例中,类型要复杂得多,因此隐藏通用 Foo<T>
并仅以新名称公开 Foo<u32>
大大提高了可用性。它还隐藏了实现细节,因此我将来可以在不破坏 API 的情况下以不同的方式实现公共类型。有没有办法用你的方法来实现?
@FlorianBrucker:“我的公共foo
函数返回Foo<u32>
的实例”——但它可以(并且可能应该)返回FooBar
的实例,从而正确隐藏Foo<u32>
实现细节。以上是关于在 Rust 库中仅公开泛型私有类型的具体变体的主要内容,如果未能解决你的问题,请参考以下文章
generics - 如何在Rust中添加一个泛型类型实现另一泛型类型的约束?
为啥 Rust 编译器要求我限制泛型类型参数的生命周期(错误 E0309)?