Constraining public types with private method

I have two types: A and B.
Both types implement a private method: fn private(&self) {}
Both types should be accepted by a public function: pub fn public(letter: A or B) {}
The implementation of public requires access to the private method.
How to type this?

To capture that the argument to public is A or B, both types could be marked with a public trait, Letter.
To capture that the argument to public implements private, both types could implement a private trait, but then this private trait would be leaked by the public function.
The argument must be constrained privately to reflect the private method, but it seems any constraint on the argument of a public function must be a public constraint...
If simple traits like this won't work, what will?

Would a wrapper enum be an option for you? Something like this:

pub struct A;
pub struct B;
    
pub enum AB {
    A(A),
    B(B),
}
    
impl AB {
    fn private(&self) {}
}
    
pub fn public(ab: AB) {
    ab.private();
}

Playground.

Your types and the public function are public, but the private method of AB isn't.

Yes, that's an option but a bit inconvenient, as it'd be nice to pass the types directly to the public function without asking library users to first wrap them (in an enum).

Jack Wrenn has a block post on how to "simulate" private trait methods with something called sealed traits.

I personally find this a little bit hacky. There is probably a good reason (maybe even rooted in the HM type system itself), why trait methods inherit the visibility of the trait itself, but I don't know why.

The public-in-private supertrait approach does not effectively hide the method. However the “Private Type Argument” approach seems interesting.

The standard library of course cheats as it has the power to (ab-)use the mechanism of unstable standard library API (besides the obvious hiding the documentation part) to fully hide its private trait methods such as this one.

I guess solutions don't get much better than that.

you do have to be careful, since just sticking it in a private module doesn't always protect it:

mod m {
    mod private {
        #[derive(Copy, Clone, Debug, Default)]
        pub struct Private;
    }

    pub trait Tr {
        fn attempted_private(_: private::Private) {
            println!("attempted private")
        }
    }

    impl Tr for () {}
}

pub use m::Tr;

fn main() {
    <()>::attempted_private(Default::default())
}

That's the “Private Value Argument” approach though, I specifically meant the approach with a type parameter from the article @jofas linked above.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.