Is it impossible to restrict visibility of trait methods from type instance?

I'm currently working on a Rust project where I have a trait A that is a super trait of B (i.e., trait A: B).

My goal is to restrict the visibility of methods in trait B such that they can only be accessed within trait A or types that implement trait B, but not expose these methods to instances that implemented trait A and B. But I didn't find a way to do so, even with the "private super-trait" pattern ---since what I want is to restrict the trait methods visibility further than just the scope of the whole crate, so it doesn't meet my aim).

I think it is a common request--- when a struct or enum type needs to implement a trait A(defined in another file), and some methods of which are tended to be used inside but not exposed to outside of the type implementing the trait.

I guess this request/feature is currently not feasible yet(Rust v1.75)?

Minor correction: trait A: B would make A a subtrait of B.

Also the private supertrait works for any scope. Just replace pub(crate) with pub(some_module) (or don't have it at all) in the examples.

But it sounds like you want the methods of a trait to be restricted to the home module of a type. I think you can do this by integrating a private type into the implementation of the trait. Not sure if a type argument or an associated type is best but you could try some stuff out.

1 Like

I wouldn’t quite be convinced by this. This sounds a bit like in OOP where there are “protected” methods visible in the class, and all classes that extend it, which is a situation in which the method, really, is barely private at all.

But maybe I’m just lacking the experience of situation where this is actually helpful. Feel free to expand on your intended use-cases.

As far as I’m aware, private API can serve purposes of encapsulation, and purposes of not exposing implementation details to API stability issues.

API stability of course couldn’t be a motivation for API that can be used in downstream crates, so now I’m facing the challenge of imagining how encapsulation (which commonly involves upholding invariants; or, again, hiding implementation details [though we’re getting into API stability domain again then, perhaps…] can be accomplished with the help of restricted visibility of trait methods).

Your first description of visibility where something “can be accessed within [a] trait” or “[within] types” goes against Rust’s usual visibility, anyway, where (except for items defined locally inside of blocks) only module boundaries serve as visibility barriers (which is in and by itself quite nice as it avoids the unnecessary conflation of barriers of abstraction and datatypes that usually happens in many traditional OOP languages). The precise meaning of visibility “within a trait” or especially “within a type” aren’t even clear to me in Rust, even if we wanted to add such a feature.

1 Like

@drewtato thanks your correction.

Also the private supertrait works for any scope. Just replace pub(crate) with pub(some_module) (or don't have it at all) in the examples.

I've tried it without pub, but then the struct can't import the module if they belong to different modules. The correct syntax for pub(some_module) I thinks is pub(in <module_name>),this way almost meets my aim, but it has a limitation-- the trait and struct(s) have to belong to the same (parent) module.

I wouldn’t quite be convinced by this. This sounds a bit like in OOP where there are “protected” methods visible in the class, and all classes that extend it , which is a situation in which the method, really, is barely private at all.

@steffahn , yes, indeed, I am trying to make it something like the protected method in some OOP language.

The precise meaning of visibility “within a trait” or especially “within a type” aren’t even clear to me in Rust, even if we wanted to add such a feature.

I mean for trait A: B, the methods defined in B can be used in A or any type implementing A, which would also be forced to implement B.


Now give it a second thought, and I would say: I agree that my aim for this protect like feature is somehow meaningless technically, because when someone can be accessible to the same crate where the trait(s) and struct(s) are defined, be he/she is responsible for defining the struct or using the struct instance, there is no need to hide any method. But practically, I still think this feature is useful:
for example, the trait A and B, struct(s) need trait A, and instances using these structs are defined in 3 modules at the same folder level (the pub(in <module_name>) way is not useful in the case),which are in the charge of 3 groups in a team, the group people using the struct(s) would be annoyed by the methods exposed from B.

Note: the reason for why trait B exists is exactly because there is some method in trait A need some abstract logic which no need to be exposed to struct(s) instance user. like this:

trait B{
  fn get_xxx_code(&self) -> u32; // how it is produced depends on the implementor
}
pub trait A:B{
  // type(struct/enums) users only need to know what the code is but don't care how it is produced
  fn print_xxx_code(&self){
     println!("the xxx code is: {}", self.get_xxx_code());
  }
}

I am not sure if this is proper example requiring the protect like feature for encapsulation, I just came up with it. Anyway, this post is just a minor case in my project, I am gonna leave it behind at present if it is not feasible.

If you just want to keep them from cluttering the namespace, you can make them associated functions like fn get_xxx_code(this: &Self) -> u32.

Thanks your advice, but that is not what I want.