【问题标题】:What's the best way to use a private type in a public function? [duplicate]在公共函数中使用私有类型的最佳方式是什么? [复制]
【发布时间】:2020-06-10 12:40:11
【问题描述】:

我有以下代码:

use std::convert::{From, Into};

#[derive(PartialEq, Debug)]
enum FindBy<'f> {
    U(&'f usize),
    S(&'f str),
    ST(&'f String),
}

impl<'f> From<&'f usize> for FindBy<'f> {
    fn from(v: &'f usize) -> Self {
        Self::U(v)
    }
}

impl<'f> From<&'f str> for FindBy<'f> {
    fn from(v: &'f str) -> Self {
        Self::S(v)
    }
}

impl TileSet {
    pub fn find<'r, 'ts: 'r, K: Into<FindBy<'r>>>(&'ts self, key: K) -> &'r Tile {
        match key.into() {
            FindBy::S(k) => &self.list.get(k).unwrap(),
            FindBy::ST(k) => &self.list.get(k).unwrap(),
            FindBy::U(k) => match &self.list.get_index(*k) {
                Some((_, v)) => &v,
                _ => todo!(),
            },
        }
    }
}

导致此警告:

warning: private type `prelude::sys::element::tile_set::FindBy<'r>` in public interface (error E0446)
  --> src/sys/element/tile_set.rs:46:5
   |
46 | /     pub fn find<'r, 'ts: 'r, K: Into<FindBy<'r>>>(&'ts self, key: K) -> &'r Tile {
47 | |         match key.into() {
48 | |             FindBy::S(k) => &self.list.get(k).unwrap(),
49 | |             FindBy::ST(k) => &self.list.get(k).unwrap(),
...  |
54 | |         }
55 | |     }
   | |_____^
   |
   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
   = note: for more information, see issue #34537 <https://github.com/rust-lang/rust/issues/34537>

FindBy 从未公开——它的目的是提供一个白名单以允许一个参数采用多种类型,但该类型本身绝不打算在外部使用,仅供内部使用,但它抱怨私有类型在公共界面中。

请允许我澄清一下,FindBy 永远不会在它所在的模块/文件之外使用,但它是函数签名的一部分,函数是 public

我不想公开 FindBy,但它从来没有公开,但因为它在公共函数中用于为参数提供类型白名单,所以 Rust 抱怨。

解决这个问题的最佳方法是什么?

【问题讨论】:

标签: rust private public


【解决方案1】:

将参数限制为几种可能类型之一的常用解决方案是使用Sealed traits

因此,对于您的 find 函数,您可以拥有 FindBy 特征(如链接中所述已密封,因此没有其他人可以实现它),而不是拥有 enum FindBy 并在其变体上调度封装了每种类型的不同逻辑,大致是这样的(未测试):

impl TileSet {
    pub fn find<K: FindBy>(&self, key: K) -> &Tile {
        key.find_in(self)
    }
}

pub trait FindBy: private::Sealed {
    fn find_in<'ts>(self, _: &'ts TileSet) -> &'ts Tile;
}

impl FindBy for &'_ usize {
    fn find_in(self, tileset: &'ts TileSet) -> &'ts Tile {
        match &tileset.list.get_index(*self) {
            Some((_, v)) => &v,
            _ => todo!(),
        }
    }
}

// impl FindBy for &'_ str { ... }
// impl FindBy for &'_ String { ... }

mod private {
    pub trait Sealed {}

    impl Sealed for &'_ usize {}
    impl Sealed for &'_ str {}
    impl Sealed for &'_ String {}
}

如果你希望它只能通过TileSet::find 使用,你也可以将方法(我称之为find_in)移动到私有特征。此外,您可能需要考虑为usize 而不是&amp;'_ usize 实现特征(但也许您有充分的理由将其作为参考)。

【讨论】:

  • 所以,公开类型并将impl TileSet更改为impl TileSet: private::Sealed
  • Shepmaster:我认为这个问题足够不同,可以有自己的答案,因为 OP 中的私有枚举实际上只存在于允许 find 将多个不同的公共类型作为其参数。您对该问题的回答没有提到密封特征,因为它们很可能不适用于那里;恕我直言,他们在这里。
  • Thermatix:不,但我会添加到我的答案中以澄清。
  • @jplatte,感谢您是唯一一个有足够理性来实际阅读问题的人。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-08-17
  • 2013-03-18
  • 2017-04-02
  • 1970-01-01
  • 2013-07-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多