Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the best way to use a private type in a public function? [duplicate]

I have the following code:

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!(),
            },
        }
    }
}

Results in this warning:

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 is never exposed — it's purpose is to provide a whitelist to allow for one argument to take multiple types, but the type itself is never intended to be used outside, internal use only, yet it's complaining of a private type in a public interface.

Allow me to clarify, FindBy is never, ever going to be used outside of the module/file it's inside of, however it's as part of the function signature and the function is public.

I don't want to expose FindBy and it never is, but because it's used in a public function to provide type whitelisting for the argument, Rust complains.

What's the best way to resolve this?

like image 914
Thermatix Avatar asked Oct 21 '25 04:10

Thermatix


1 Answers

The usual solution to restricting a parameter to one of a few possible types is to use Sealed traits.

So, for your find function, instead of having an enum FindBy and dispatching on its variant, you could have a trait FindBy (that's sealed as explained in the link, so nobody else can implement it) that encapsulates the different logic for each of the types, roughly like this (not tested):

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

You could also move the method (that I called find_in) to the private trait if you want it to only be usable through TileSet::find. Also, you might want to consider implementing the trait for usize rather than &'_ usize (but maybe you have a good reason for it to be a reference).

like image 173
jplatte Avatar answered Oct 24 '25 15:10

jplatte