1
0
mirror of https://github.com/google/comprehensive-rust.git synced 2025-07-11 16:50:16 +02:00

Parse, don't validate

This commit is contained in:
LukeMathWalker
2025-07-02 16:34:33 +02:00
parent e262c0f195
commit ac219256b5
2 changed files with 64 additions and 0 deletions

View File

@ -435,6 +435,7 @@
- [Leveraging the Type System](idiomatic/leveraging-the-type-system.md)
- [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)
---

View File

@ -0,0 +1,63 @@
---
minutes: 5
---
# Parse, Don't Validate
The newtype pattern can be leveraged to enforce _invariants_.
```rust
pub struct Username(String);
impl Username {
pub fn new(username: String) -> Result<Self, InvalidUsername> {
if username.is_empty() {
return Err(InvalidUsername::CannotBeEmpty)
}
if username.len() > 32 {
return Err(InvalidUsername::TooLong { len: username.len() })
}
// Other validation checks...
Ok(Self(username))
}
pub fn as_str(&self) -> &str {
&self.0
}
}
# pub enum InvalidUsername {
# CannotBeEmpty,
# TooLong { len: usize },
# }
```
<details>
- The newtype pattern, combined with Rust's module and visibility system, can be
used to _guarantee_ that instances of a given type satisfy a set of
invariants.\
In the example above, the raw `String` stored inside the
`Username` struct can't be accessed directly from other modules or crates,
since it's not marked as `pub` or `pub(in ...)`. Consumers of the `Username`
type are forced to use the `new` method to create instances. In turn, `new`
performs validation, thus ensuring that all instances of `Username` satisfy
those checks.
- The `as_str` method allows consumers to access the raw string representation
(e.g. to store it in a database) but, thanks to Rust's borrow checker,
they can't modify it.
- Stress the importance of evaluating _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 the invariants. For example, if the `Username`
type implements the `DerefMut` trait, users can modify the underlying string
directly, bypassing the validation checks in `new`.
- Type-level invariants have second-order benefits.\
The input is validated once, at the boundary, and the rest of the program can rely
on the invariants being upheld. We can avoid redundant validation and
"defensive programming" checks throughout the program, reducing noise and
improving performance.
</details>