From c822a568ffb926b474157201c0127eb99c518321 Mon Sep 17 00:00:00 2001 From: Rubens Brandao Date: Thu, 6 Apr 2023 11:59:55 -0300 Subject: [PATCH 1/6] Add Sealed trait pattern --- SUMMARY.md | 1 + patterns/behavioural/sealed.md | 74 ++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 patterns/behavioural/sealed.md diff --git a/SUMMARY.md b/SUMMARY.md index 2c73804f..62a6d198 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -28,6 +28,7 @@ - [Interpreter](./patterns/behavioural/interpreter.md) - [Newtype](./patterns/behavioural/newtype.md) - [RAII Guards](./patterns/behavioural/RAII.md) + - [Sealed Trait](./patterns/behavioural/sealed.md) - [Strategy](./patterns/behavioural/strategy.md) - [Visitor](./patterns/behavioural/visitor.md) - [Creational](./patterns/creational/intro.md) diff --git a/patterns/behavioural/sealed.md b/patterns/behavioural/sealed.md new file mode 100644 index 00000000..50be9935 --- /dev/null +++ b/patterns/behavioural/sealed.md @@ -0,0 +1,74 @@ +# 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 + +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 +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://predr.ag/blog/definitive-guide-to-sealed-traits-in-rust/) about sealed, private and other pattern for traits. From a839842fe1dc8506064b91d62ea58b69a1c8648b Mon Sep 17 00:00:00 2001 From: Rubens Brandao Date: Thu, 6 Apr 2023 17:57:50 -0300 Subject: [PATCH 2/6] Fix rust sealed example --- patterns/behavioural/sealed.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/patterns/behavioural/sealed.md b/patterns/behavioural/sealed.md index 50be9935..e5bc26f1 100644 --- a/patterns/behavioural/sealed.md +++ b/patterns/behavioural/sealed.md @@ -31,10 +31,10 @@ 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 +```rust,ignore pub trait OsStrExt: Sealed { fn from_bytes(slice: &[u8]) -> &Self; - fn as_bytes(&self) -> &[u8] + fn as_bytes(&self) -> &[u8]; } ``` From 657df006ff77187bb480153a62b2f522ad64a772 Mon Sep 17 00:00:00 2001 From: Rubens Brandao Date: Thu, 6 Apr 2023 18:03:14 -0300 Subject: [PATCH 3/6] Fix sealed markdown --- patterns/behavioural/sealed.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/patterns/behavioural/sealed.md b/patterns/behavioural/sealed.md index e5bc26f1..5d23a2cf 100644 --- a/patterns/behavioural/sealed.md +++ b/patterns/behavioural/sealed.md @@ -28,9 +28,13 @@ sealed trait to only happen locally. ## Example 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). +`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; @@ -47,7 +51,6 @@ 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 @@ -71,4 +74,6 @@ without compromising the compatibility with existing code uses of it. ## See also -Blog post from [Predrag](https://predr.ag/blog/definitive-guide-to-sealed-traits-in-rust/) about sealed, private and other pattern for traits. +Blog post from +[Predrag](https://predr.ag/blog/definitive-guide-to-sealed-traits-in-rust/) +about sealed, private and other pattern for traits. From ca7d28fec8339460277da794db0aba73b3618e3f Mon Sep 17 00:00:00 2001 From: simonsan <14062932+simonsan@users.noreply.github.com> Date: Fri, 7 Apr 2023 16:00:45 +0200 Subject: [PATCH 4/6] Update patterns/behavioural/sealed.md --- patterns/behavioural/sealed.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/patterns/behavioural/sealed.md b/patterns/behavioural/sealed.md index 5d23a2cf..99298b42 100644 --- a/patterns/behavioural/sealed.md +++ b/patterns/behavioural/sealed.md @@ -75,5 +75,5 @@ without compromising the compatibility with existing code uses of it. ## See also Blog post from -[Predrag](https://predr.ag/blog/definitive-guide-to-sealed-traits-in-rust/) +[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. From 8ccf232894fee9eba3cabe8d8448f616205803c6 Mon Sep 17 00:00:00 2001 From: Rubens Brandao Date: Fri, 7 Apr 2023 18:42:31 -0300 Subject: [PATCH 5/6] Add new simpler example on sealed trait --- src/patterns/behavioural/sealed.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/patterns/behavioural/sealed.md b/src/patterns/behavioural/sealed.md index 99298b42..6694fe82 100644 --- a/src/patterns/behavioural/sealed.md +++ b/src/patterns/behavioural/sealed.md @@ -27,6 +27,31 @@ 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 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), From 520824308a9f418ec3a60c6e18f8b6f0ea51b852 Mon Sep 17 00:00:00 2001 From: Rubens Brandao Date: Fri, 7 Apr 2023 19:03:12 -0300 Subject: [PATCH 6/6] Fix markdown on sealed trait --- src/patterns/behavioural/sealed.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/patterns/behavioural/sealed.md b/src/patterns/behavioural/sealed.md index 6694fe82..45b90f86 100644 --- a/src/patterns/behavioural/sealed.md +++ b/src/patterns/behavioural/sealed.md @@ -74,7 +74,7 @@ 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. +> This is so that future additional methods are not breaking changes. ## Advantages