Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to have a public trait with a pub(crate) method in a library?

Suppose I have the following trait in a library:

pub trait Foo {
    fn public_op(&self);
    fn internal_op(&self);
}

This trait is then implemented for a bunch of structs in this library:

pub struct One {}

impl Foo for One {
    fn public_op(&self) {}
    fn internal_op(&self) {}
}

pub struct Two {}

impl Foo for Two {
    fn public_op(&self) {}
    fn internal_op(&self) {}
}

And there is a public function in this library which receives the trait type:

pub fn process(obj: &dyn Foo) {
    obj.public_op();
    obj.internal_op();
}

The problem is that, since Foo trait is public in the library, the method internal_op is also public... but in fact it should have pub(crate) visibility, because it must be used inside the library only.

As far as I know, all methods of a trait are public, so how can I redesign this problem?

like image 441
rodrigocfd Avatar asked Oct 11 '25 15:10

rodrigocfd


1 Answers

You can split Foo into two traits, one public and one private.

pub trait Foo: private::Foo {
    fn public_op(&self);
}

pub(crate) mod private {
    pub trait Foo {
        fn internal_op(&self);
    }
}

Then implement them both in your library crate as follows:

pub struct One {}

impl Foo for One {
    fn public_op(&self) {}
}

impl private::Foo for One {
    fn internal_op(&self) {}
}

Then using from outside the library crate would look like:

fn main() {
   let one = One {};
   one.public_op(); // works
   process(&one); // works
   
   //one.internal_op(); // error[E0599]: no method named `internal_op`...
}

This does mean that its impossible for users of your library to implement Foo as Foo is now effectively a 'sealed trait'.

See Jack Wrenn's blog post for a discussion of this and alternative approaches.

Rust playground link

like image 153
willjcroz Avatar answered Oct 15 '25 01:10

willjcroz