This is related to a design question I have in the user space.
Traits are great for open interfaces, but how to use them for a closed set of possibilities?
In current Rust, I can find only one model to hide implementation details in my scenario, abusing a Private supertrait. Rust is marching towards progress, and this bug will be fixed eventually. (Edit: See below, maybe this case is not a bug)
Judging from the PR, lots of crates prefer to keep implementation details private, sometimes it’s really unfortunate when they have to be public.
// Marker types A and B
pub enum A { }
pub enum B { }
pub trait Marker: PrivateMarker { }
impl Marker for A { }
impl Marker for B { }
// Problem: How to use only the bound M: Marker,
// but still use a private trait implemented by A and B?
mod internal {
// The privatemarker trait is private --
// we want full freedom in changing the implementation details.
pub trait PrivateMarker {
fn private();
}
}
use internal::PrivateMarker;
Is there a language feature on the horizon to help with this? I’m not sure specialization covers it; in this case we just want to close the trait to implementors A and B, and that’s it.
I don’t think there is a fundamental problem in a public trait that can’t be implemented by the user. Traits have many different roles, and this is a role I’d like them to fill better; API flexibility without being open for extension.
Do you want the trait to be private for a crate or for a module?
In the first case private inner module solves the problem - the trait inside it will be unnameable and therefore unimplementable for other crates.
As for the second case, private traits without associated types (and without supertraits with associated types) potentially can be permitted in bounds in public interfaces. I’m pretty sure they can’t leak anything private, only cause dead links in rustdoc.
Currently, I believe the following works (until specialization, that is…):
pub trait PrivateMarker {}
trait NoImpl {}
impl<T> PrivateMarker for T where T: NoImpl {}
pub trait Test: PrivateMarker { }
impl Test for () {}
// Coherence guarantees that this can only be
// implemented in this crate.
impl PrivateMarker for () {}
Regardless, this is a hack. I’d like a way to mark a trait as closed (shared code only).
is permitted, so errors from the private-in-public checker can always be easily worked around if necessary. The main purpose of this checker is to guarantee that non-pub things can’t escape their module, not to guarantee nameability.
Thanks for explaining! Given the range of things uncovered by the new checking, I wasn’t sure which quirks would remain as features. Please let’s commit to that this stays then?
I put the code in a private module, then create a public module that acts as a facade (for example the fbo module (“fbo” means “framebuffer object”) is private, and the framebuffer module is public).
But all these are crappy, and I would love to get some language improvements.