【问题标题】:Exposing only a concrete variant of a generic private type in a Rust library在 Rust 库中仅公开泛型私有类型的具体变体
【发布时间】: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) 一起使用,但背后的主要想法确实有效。

标签: generics rust access-modifiers


【解决方案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; 实现细节。
猜你喜欢
  • 2022-01-10
  • 2020-08-15
  • 2018-09-20
  • 2019-03-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多