diff --git a/src/SUMMARY.md b/src/SUMMARY.md index ce0eeb03..1950476a 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -436,6 +436,7 @@ - [Newtype Pattern](idiomatic/leveraging-the-type-system/newtype-pattern.md) - [Semantic Confusion](idiomatic/leveraging-the-type-system/newtype-pattern/semantic-confusion.md) - [Parse, Don't Validate](idiomatic/leveraging-the-type-system/newtype-pattern/parse-don-t-validate.md) + - [Is It Encapsulated?](idiomatic/leveraging-the-type-system/newtype-pattern/is-it-encapsulated.md) --- diff --git a/src/idiomatic/leveraging-the-type-system/newtype-pattern/is-it-encapsulated.md b/src/idiomatic/leveraging-the-type-system/newtype-pattern/is-it-encapsulated.md new file mode 100644 index 00000000..f119df3e --- /dev/null +++ b/src/idiomatic/leveraging-the-type-system/newtype-pattern/is-it-encapsulated.md @@ -0,0 +1,54 @@ +--- +minutes: 5 +--- + +# Is It Truly Encapsulated? + +You must evaluate the entire API surface exposed by a newtype to determine if +invariants are indeed bullet-proof. It is crucial to consider all possible +interactions, including trait implementations, that may allow users to bypass +validation checks. + +```rust +pub struct Username(String); + +impl Username { + pub fn new(username: String) -> Result { + // Validation checks... + Ok(Self(username)) + } +} + +impl DerefMut for Username { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} +# impl Deref for Username { +# type Target = str; +# +# fn deref(&self) -> &Self::Target { +# &self.0 +# } +# } +# pub struct InvalidUsername; +``` + +
+ +- `DerefMut` allows users to get a mutable reference to the wrapped type. + + The mutable reference can be used to modify the underlying data in ways that + may violate the invariants enforced by `Username::new`! + +- When auditing the API surface of a newtype, you can narrow down the review + scope to methods and traits that provide mutable access to the underlying + data. + +- Remind students of privacy boundaries. + + In particular, functions and methods defined in the same module of the newtype + can access its underlying data directly. If possible, move the newtype + definition to its own separate module to reduce the scope of the audit. + +