-
Notifications
You must be signed in to change notification settings - Fork 377
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
Add Inner Trait pattern #355
Closed
Closed
Changes from all commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
c822a56
Add Sealed trait pattern
rbran a839fae
Add Inner Trait pattern
rbran a839842
Fix rust sealed example
rbran 657df00
Fix sealed markdown
rbran 1cfc5d8
Fix inner-trait markdown
rbran ca7d28f
Update patterns/behavioural/sealed.md
simonsan a080f0c
Merge branch 'main' of https://github.com/rust-unofficial/patterns in…
rbran 32511be
Merge branch 'sealed-trait' of github.com:rbran/patterns into sealed-…
rbran 8ccf232
Add new simpler example on sealed trait
rbran 5208243
Fix markdown on sealed trait
rbran 81c1d58
Merge branch 'main' into sealed-trait
rbran 07b0105
Merge branch 'main' of https://github.com/rust-unofficial/patterns in…
rbran 1b49998
Merge branch 'sealed-trait' of github.com:rbran/patterns into inner-t…
rbran 439da97
Simplify innet trait example
rbran File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
# 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 speed(&self) -> f64; | ||
fn accelerate(&mut self, duration: f64); | ||
} | ||
//not public | ||
mod inner_lib { | ||
// trait that is only accessible to this crate | ||
pub(crate) trait InnerCar { | ||
fn speed(&self) -> f64; | ||
//private | ||
fn set_speed(&mut self, new_speed: f64); | ||
//private | ||
fn acceleration(&self) -> f64; | ||
fn accelerate(&mut self, duration: f64) { | ||
self.set_speed( | ||
self.speed() + (self.acceleration() * duration) | ||
); | ||
} | ||
} | ||
//Auto implement Car for all InnerCar, by forwarding the Car trait to the | ||
//InnerCar implementation | ||
impl<T: InnerCar> crate::Car for T { | ||
fn speed(&self) -> f64 { | ||
<Self as InnerCar>::speed(self) | ||
} | ||
fn accelerate(&mut self, duration: f64) { | ||
<Self as InnerCar>::accelerate(self, duration) | ||
} | ||
} | ||
} | ||
|
||
#[derive(Default)] | ||
pub struct Car1(f64); | ||
//is not necessary to implement `accelerate`, as inner_trait can do that. | ||
impl inner_lib::InnerCar for Car1 { | ||
fn speed(&self) -> f64 {self.0} | ||
fn set_speed(&mut self, new_speed: f64) {self.0 = new_speed} | ||
fn 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. | ||
|
||
## 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/) | ||
about sealed, private and other patterns for traits. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
# Sealed Trait | ||
|
||
## Description | ||
|
||
In Rust, a sealed trait is a trait that can only be implemented within the same | ||
crate where it is defined. It is achieved by making a public trait that depends | ||
on a [supertrait](https://doc.rust-lang.org/rust-by-example/trait/supertraits.html) | ||
that is private or only public on the local crate. This restricts the ability | ||
to implement the trait from outside the crate and provides a form of interface | ||
segregation. | ||
|
||
## Motivation | ||
|
||
Sealed traits can be used to create a set of behaviors that that allow future | ||
addition of methods without breaking the API. | ||
|
||
The sealed trait pattern helps to separate the public interface from the | ||
implementation details. This makes it easier to maintain and evolve the | ||
implementation over time without breaking existing code. It also provides a | ||
level of abstraction that allows users to interact with the library or module at | ||
a high level, without needing to know the implementation details. | ||
|
||
Because users of this crate can't implement this trait directly, is possible to | ||
add methods to it, without break existing code using this create. Only | ||
implementations of this trait will need updated, what is assured by the | ||
sealed trait to only happen locally. | ||
|
||
## Example | ||
|
||
One possible use of the sealed trait is to limit what kind of implementation a | ||
function can receive, allowing only a limited number of types to be passed as | ||
parameters. | ||
|
||
```rust,ignore | ||
pub(crate) mod private { | ||
pub(crate) trait Sealed {} | ||
} | ||
// MyStruct is Sealed, and only this crate have access to it. Other crates will | ||
// be able to implement it. | ||
pub trait MyStruct: private::Sealed {...} | ||
// auto implement Sealed for any type that implement MyStruct | ||
impl<T: MyStruct> private::Sealed for T {} | ||
|
||
pub struct MyStructA {...} | ||
impl MyStruct for MyStructA {...} | ||
|
||
pub struct MyStructB {...} | ||
impl MyStruct for MyStructB {...} | ||
|
||
// this function will only receive MyStructA or MyStructB because they are the | ||
// only ones that implement the MyStruct trait | ||
pub fn receive_my_struct(my_struct: impl MyStruct) {...} | ||
``` | ||
|
||
The standard library makes use of a sealed trait, one example is the | ||
`OsStrExt` trait for | ||
[unix](https://doc.rust-lang.org/std/os/unix/ffi/trait.OsStrExt.html), | ||
[windows](https://doc.rust-lang.org/std/os/windows/ffi/trait.OsStrExt.html) and | ||
[wasi](https://doc.rust-lang.org/std/os/wasi/ffi/trait.OsStrExt.html). | ||
|
||
Trait from `std::os::unix::ffi::OsStrExt`: | ||
|
||
```rust,ignore | ||
pub trait OsStrExt: Sealed { | ||
fn from_bytes(slice: &[u8]) -> &Self; | ||
fn as_bytes(&self) -> &[u8]; | ||
} | ||
``` | ||
|
||
The `Sealed` trait is private and cannot be accessed from outside the standard | ||
library. Not allowing users to implement `OsStrExt` for any type, except for the | ||
implementations already present on the standard library. | ||
|
||
The documentation describes it's motivation as the following: | ||
|
||
> This trait is sealed: it cannot be implemented outside the standard library. | ||
> This is so that future additional methods are not breaking changes. | ||
|
||
## Advantages | ||
|
||
By separating the public interface from the implementation details, it is | ||
easier to maintain, ensure that the code remains correct without affecting | ||
external users. | ||
|
||
## Disadvantages | ||
|
||
Although it usually reduces complexity and code duplication, the sealed trait | ||
pattern can add complexity to the codebase, particularly if there are many | ||
sealed traits that need to be managed. | ||
|
||
## Discussion | ||
|
||
Sealed traits are a useful tool for creating a set of related behaviors that are | ||
intended to be used together without allowing other behaviors to be added from | ||
outside the crate. | ||
|
||
This restriction also allow future additions to the trait | ||
without compromising the compatibility with existing code uses of it. | ||
|
||
## See also | ||
|
||
Blog post from | ||
[Predrag](https://web.archive.org/web/20230406211349/https://predr.ag/blog/definitive-guide-to-sealed-traits-in-rust/) | ||
about sealed, private and other pattern for traits. | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
This file belongs to #354 as well. Do you want to PR it all in one PR, or you rather want it separated? If so, then removal of this file would be good.