在 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
}

Playground

我反对 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。用户赢,你赢。