Learn
Best Practices
Newtypes

Newtypes

In programming, a common dilemma is managing primitive data types and ensuring that they conform to certain constraints. The Newtype pattern emerges as a solution, allowing developers to wrap primitive types in a way that conveys more meaning and enforces tighter constraints. For instance, instead of managing generic number types, we can create types like PositiveInteger and NonZeroNumber, making our programs more robust and semantically rich.

The Newtype pattern involves creating a new type that wraps around an existing type. It doesn’t alter the representation of the enclosed type but adds a new layer of meaning and constraints to it. This helps in avoiding logical errors related to the misuse of types, such as passing a negative number where a positive number is expected.

ℹ️

It's a good practice to use the Newtype as soon as possible. For example, if you have a UserId to represent a user's UUID, you should use it as soon as you get the UUID from the database. This way, you can avoid passing the UUID around and accidentally using it as a PostId or CommentId.

How to Implement

struct NonEmptyString(String);
 
impl NonEmptyString {
    fn new(s: String) -> Option<Self> {
        if s.is_empty() {
            None
        } else {
            Some(Self(s))
        }
    }
}