-
Notifications
You must be signed in to change notification settings - Fork 404
Add Inner Trait pattern #355
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
c822a56
a839fae
a839842
657df00
1cfc5d8
ca7d28f
a080f0c
32511be
8ccf232
5208243
81c1d58
07b0105
1b49998
439da97
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,105 @@ | ||
| # Inner Trait | ||
|
|
||
| ## Description | ||
|
|
||
| It is possible to define a private trait that implements all the | ||
| methods of a public trait and also includes some private functions. This pattern | ||
| can be used to provide additional functionality to the implementation of a | ||
| public trait while keeping the private methods hidden from the public API. | ||
|
|
||
| ## Example | ||
|
|
||
| This example demonstrate how a public trait `Car` can be implemented and include | ||
| extra private methods using a auxiliary private trait `InnerCar`. | ||
|
|
||
| ```rust,ignore | ||
| // trait that is public and part of the API | ||
| pub trait Car { | ||
| fn get_speed(&self) -> f64; | ||
rbran marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| fn accelerate(&mut self, duration: f64); | ||
| fn brake(&mut self, force: f64); | ||
|
||
| } | ||
| //not public | ||
| mod inner_lib { | ||
| // trait that is only accessible to this crate | ||
| pub(crate) trait InnerCar { | ||
| fn get_speed(&self) -> f64; | ||
| //private | ||
| fn set_speed(&mut self, new_speed: f64); | ||
| //private | ||
| fn get_acceleration(&self) -> f64; | ||
| fn accelerate(&mut self, duration: f64) { | ||
| self.set_speed( | ||
| self.get_speed() + (self.get_acceleration() * duration) | ||
| ); | ||
| } | ||
| fn brake(&mut self, force: f64) { | ||
| self.set_speed( | ||
| self.get_speed() - (force * self.get_acceleration()) | ||
| ); | ||
| } | ||
| } | ||
| //Auto implement Car for all InnerCar, by forwarding the Car trait to the | ||
| //InnerCar implementation | ||
| impl<T: InnerCar> crate::Car for T { | ||
| fn get_speed(&self) -> f64 { | ||
| <Self as InnerCar>::get_speed(self) | ||
| } | ||
| fn accelerate(&mut self, duration: f64) { | ||
| <Self as InnerCar>::accelerate(self, duration) | ||
| } | ||
| fn brake(&mut self, force: f64) { | ||
| <Self as InnerCar>::brake(self, force) | ||
| } | ||
| } | ||
| } | ||
| #[derive(Default)] | ||
| pub struct Car1(f64); | ||
| //is not necessary to implement `accelerate` and `brake`, as inner_trait can do that. | ||
| impl inner_lib::InnerCar for Car1 { | ||
| fn get_speed(&self) -> f64 {self.0} | ||
| fn set_speed(&mut self, new_speed: f64) {self.0 = new_speed} | ||
| fn get_acceleration(&self) -> f64 {0.10} | ||
| } | ||
| //more Car implementations... | ||
| ``` | ||
|
|
||
| ## Motivation | ||
|
|
||
| This pattern allows developers to provide additional functionality to the | ||
| implementation of a public trait without exposing that functionality as part of | ||
| the public API. By using a private trait, developers can also improve the | ||
| reusability of their code, since the private functionality can be reused across | ||
| multiple implementations of the public trait. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suppose I'm using a trait
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is another layer on top of this is to prevent breaking changes, since we can now limit functionality without exposing public API. |
||
|
|
||
| ## Advantages | ||
|
|
||
| - Provides hidden functionality while keeping the private methods from the API. | ||
| - Improves modularity by separating public and private functionality. | ||
| - Increases code reusability, since the private functionality can be reused. | ||
|
|
||
| ## Disadvantages | ||
|
|
||
| - Can be harder to understand if the private trait are not well documented. | ||
| - Can lead to tight coupling between the public and private functionality. | ||
|
|
||
| ## Discussion | ||
|
|
||
| This pattern is very similar to the concept of "interfaces" and "abstract types" | ||
| with public/private methods in object-oriented programming. | ||
|
|
||
| In object-oriented programming (OOP), private methods can be used to encapsulate | ||
| implementation details within an interface, while public methods exposes | ||
| functionality. | ||
|
|
||
| In rust there is the public/private trait analogous, the private trait | ||
| implementing the hidden functionalities, while the public trait exposes | ||
| functionality. | ||
|
|
||
| ## See also | ||
|
|
||
| Wikipedia [OOP Interface](https://en.wikipedia.org/wiki/Interface_%28object-oriented_programming%29). | ||
|
|
||
| Blog post from [Predrag](https://predr.ag/blog/definitive-guide-to-sealed-traits-in-rust/) | ||
simonsan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| about sealed, private and other patterns for traits. | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we probably want to show a real example in standard library or in popular crates for this.