Search code examples
design-patternsrusttraits

How to have a private part of a trait?


In a crate I write, I have a bunch of internal structs public to the user and that share some code. Some of the shared code is public, some is an internal implementation. To share efficiently the code, I am using macros, but now that the project has more features, this begins to be messy, and I am not satisfied by the semantic of this.

I would like to use a trait, but without exposing the implementation. For example:

pub trait MyTrait {
    type Next;

    // This function is for the user.
    fn forward(&self) -> Self::Next {
        self.do_the_job()
    }

    // This function is for the user.
    fn stop(&self) {
        self.do_the_job();
    }

    // This function is an implementation detail.
    fn do_the_job(&self) -> Self::Next;
}

I want the user to see and use forward and stop, but not do_the_job, while my data would only implement do_the_job.

Is it possible to design my code to do something like that? I have tried to imagine some solutions, but nothing has come to my mind.

Playground


In an object oriented language with inheritance, I would do (pseudo code):

public interface MyTrait {
    type Next;

    fn forward(&self) -> Self::Next;

    fn stop(&self);
}

public abstract class MyCommonCode extends MyTrait {
    fn forward(&self) -> Self::Next {
        self.do_the_job()
    }

    fn stop(&self) {
        self.do_the_job();
    }

    protected abstract fn do_the_job(&self) -> Self::Next;
}

public MyType extends MyCommonCode {
    type Next = i32;

    protected override fn do_the_job(&self) -> Self::Next {
        // etc.
    }
}

Solution

  • Traits are similar to interfaces:

    Traits are Rust’s sole notion of interface.

    An interface is meant to document available methods, to have an interface with private methods makes no sense. Correspondingly, in Rust you can't have different levels of visibility in one trait. If you can see the trait, you can always see all of it. However, Rust traits are subtly different from interfaces: they combine declarations and implementations. I see how it would be intuitive to have a trait with some private functions.

    For some time it was possible to split a trait into a public and private part. You would have two traits, one containing your public interface, the other with your private functionality, but this is being removed in newer versions of Rust.

    The current workaround is still splitting the trait, but the private part must now be represented by a public trait within a private module. To explain this, here is some sample code:

    // this module contains a public trait Inc, to increment a value
    // and it implements it by using a private trait Add
    mod my_math {
        pub struct Val {
            pub val: i32,
        }
    
        // this is necessary to encapsulate the private trait
        // the module is private, so the trait is not exported
        mod private_parts {
            pub trait Add {
                fn add(&mut self, i32);
            }
        }
    
        // in the following code, we have to use adequate namespacing
        impl private_parts::Add for Val {
            fn add(&mut self, other: i32) {
                self.val += other;
            }
        }
    
        pub trait Inc: private_parts::Add {
            fn inc(&mut self);
        }
    
        impl Inc for Val {
            fn inc(&mut self) {
                use my_math::private_parts::Add;
                self.add(1)
            }
        }
    }
    
    fn main() {
        use my_math::Inc;
        let mut b = my_math::Val { val: 3 };
        println!("value: {}", b.val);
        b.inc();
        println!("value: {}", b.val);
    }