在 public 函数中使用私有类型的最佳方式是什么?
What's the best way to use a private type in a public function?
我有以下代码:
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
永远不会暴露——它的目的是提供一个白名单以允许一个参数采用多种类型,但类型本身永远不会在外部使用,仅供内部使用,但它在抱怨public 接口中的私有类型。
请允许我澄清一下,FindBy
永远不会在其内部的 module/file 之外使用,但是它作为函数签名的一部分并且函数是 public
.
我不想公开 FindBy
而且它从来没有公开过,但是因为它在 public 函数中用于为参数提供类型白名单,Rust 抱怨道。
解决此问题的最佳方法是什么?
将参数限制为几种可能类型之一的常用解决方案是使用 Sealed traits.
因此,对于您的 find
函数,您可以拥有特征 FindBy
(如 link,所以没有其他人可以实现它),它封装了每种类型的不同逻辑,大致如下(未测试):
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
而不是 &'_ usize
实现特征(但也许您有充分的理由将其作为参考)。
我有以下代码:
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
永远不会暴露——它的目的是提供一个白名单以允许一个参数采用多种类型,但类型本身永远不会在外部使用,仅供内部使用,但它在抱怨public 接口中的私有类型。
请允许我澄清一下,FindBy
永远不会在其内部的 module/file 之外使用,但是它作为函数签名的一部分并且函数是 public
.
我不想公开 FindBy
而且它从来没有公开过,但是因为它在 public 函数中用于为参数提供类型白名单,Rust 抱怨道。
解决此问题的最佳方法是什么?
将参数限制为几种可能类型之一的常用解决方案是使用 Sealed traits.
因此,对于您的 find
函数,您可以拥有特征 FindBy
(如 link,所以没有其他人可以实现它),它封装了每种类型的不同逻辑,大致如下(未测试):
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
而不是 &'_ usize
实现特征(但也许您有充分的理由将其作为参考)。