Can I safely rely on this trick?


#1

My glium library is a wrapper around OpenGL. It provides a nice and convenient API, but under the hood there is a big machinery to make everything work that I’d like to keep private.

But because Rust’s visibility system is a bit limited, I often need to rely on enums. For example instead of having a method on a trait that interacts with OpenGL (which is something that I don’t want because I would need to expose a lot of the internal machinery), instead that trait has a method that builds an enum and this enum has a private method that interacts with OpenGL. This is unfortunately sub-optimal.

Now here comes the question. I have recently discovered that you could write this:

mod a {
    trait T {}
    pub trait U: T {}
}

And it works! Users won’t be able to implement U for their own type and that allows me to distinguish between public and “private” methods, as I’d put the public methods in U and the “private” methods in T. With this pattern, I will finally be able to get zero-cost abstractions.

But can I safely use this trick, or is there a chance that it is going to be considered as a bug and get removed?


#2

There is a closely related bug:

mod a {
    pub trait T {}
}

You can use a::T in public API and it will not trip the “use of private type / trait in public API”, because it’s fooled by the pub even if it’s only internal to the crate.

I suspect many rust crates rely on this by mistake. I think some of my crates do…


#3

By the way, there are two other solutions that I think are more standard that you could consider:

If you need certain guarantees from the trait implementors, and you need to trust them, use unsafe trait.

If you just want to make it clear that certain methods are not to be used in a trait, you can use #[doc(hidden)] on those methods. As long as your communication is clear, I’d break & change those methods with no shame – they are explicitly not part of your stable API.


#4

I’m a bit confused about the use case. If the separation of traits is just a “hack” to indicate the user shouldn’t override the methods in the parent trait - as opposed to it being necessary to have two traits because they’re implemented in separate locations - then it seems like you don’t want those methods to be overridden at all. In that case, you could use a generic impl of a separate trait, which is a bit more semantically accurate anyway:

trait PrivateMethods { ... }
impl<T: OverridableMethods> PrivateMethods for T { ... }

…but I may be misunderstanding the situation.


#5

My use case is that the private method looks for example like this:

trait MyTrait {
    fn foo(&self,  &mut CommandContext, &OtherInternalType, libc::c_int);
}

I don’t want to expose CommandContext or OtherInternalType to the user because that would require exposing almost all of my library’s internals (which is mostly unsafe code). Even with the #[doc(hidden)] solution I’d need to add this attribute everywhere.


#6

I’ve been handling communication between the various parts of the library with private traits at the crate root. But as I said, this is not optimal in term of performance.

I know about unsafe traits, about #[doc(hidden)], about implementing a difficult-to-implement trait on another easy easy-to-implent trait, and even several other tricks (I almost wanted to write a “Rust tips and tricks” article, but I’m too lazy)

A good part of my work on glium is being able to handle Rust’s limitations when it comes to the visibility system. In safe libraries I’d just expose everything and don’t care, but here it’s just far too unsafe and complex to be exposable.