在 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&lt;u8&gt;Foo&lt;u32&gt;),但 Foo 仅作为 Foo&lt;u32&gt; 出现在 lib 的公共 API 中。

像我目前所做的那样公开通用Foo 会使lib 的公共API 及其文档变得不必要地复杂:用户永远不会从不是Foo&lt;u32&gt; 的库中获得Foo。因此,我想以某种方式只公开和公开记录Foo&lt;u32&gt;(最好使用不同的非通用名称,例如Bar)并将Foo设为私有。

我尝试过使用类型别名 (type Bar = Foo&lt;u32&gt;),但似乎它们会自动扩展为 cargo doc(而且它们似乎也没有单独的可见性)。

我可能会复制Foo&lt;X&gt; 的定义并将其命名为Bar,然后为Bar 实现类似From&lt;Foo&lt;u32&gt;&gt; 的东西。但是,我对Foo&lt;X&gt; 的实际定义相当复杂,所以我想避免这种情况。

还有其他方法可以实现我的目标吗?

【问题讨论】:

将模块公开给 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 docFooBar 生成的文档。但是,这可能是一个不同的问题,所以我发布了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&lt;u32&gt; 设为FooBar 中的私有字段,将Foo&lt;u32&gt; 隐藏在公共API 中。但是,我的 API 的用户需要访问Foo&lt;u32&gt;(我的公共foo 函数返回Foo&lt;u32&gt; 的实例)。在我的实际用例中,类型要复杂得多,因此隐藏通用 Foo&lt;T&gt; 并仅以新名称公开 Foo&lt;u32&gt; 大大提高了可用性。它还隐藏了实现细节,因此我将来可以在不破坏 API 的情况下以不同的方式实现公共类型。有没有办法用你的方法来实现? @FlorianBrucker:“我的公共foo 函数返回Foo&lt;u32&gt; 的实例”——但它可以(并且可能应该)返回FooBar 的实例,从而正确隐藏Foo&lt;u32&gt; 实现细节。

以上是关于在 Rust 库中仅公开泛型私有类型的具体变体的主要内容,如果未能解决你的问题,请参考以下文章

generics - 如何在Rust中添加一个泛型类型实现另一泛型类型的约束?

为啥 Rust 编译器要求我限制泛型类型参数的生命周期(错误 E0309)?

Rust入坑指南:海纳百川

通用函数和类型如何存储在 rlib 中?

为啥 Rust 认为我的私有类型必须是公共的,除非我使用 pub(crate)?

如何在 crate 中将 Rust 项目设为公开,但在其外部设为私有?