The future of privacy and encapsulation


#1

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;

Rust Playground Link

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.


Pre-RFC: Sealed traits
#2

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.


#3

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).


#4

Isn’t this one of the cases that will be disallowed? If not, that’s great news!


#5
mod inner { pub struct OtherCratesCantNameMe; }
pub fn f() -> inner::OtherCratesCantNameMe { ... }

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.


#6

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?


#7

I assume that this will eventually be reachable through f::Output.


#8

Without exaggeration, one of the biggest challenges of writing glium is to handle privacy.

I’m using several techniques:

But all these are crappy, and I would love to get some language improvements.