In this lesson, you will learn how to build sophisticated domain models by composing "Products" and "Sums" to represent complex real-world logic with absolute type safety.
The Building Blocks
Algebraic Data Types (ADTs) are the foundation of Rust’s type system. They are categorized into two primary forms:
- Product Types (Structs): These represent "AND" relationships. A
structcontains multiple fields; to construct it, you must provide values for every field. If aUserhas aname(String) AND anage(u32), that is a product. - Sum Types (Enums): These represent "OR" relationships. An
enumcan hold one of several variants. If aPaymentMethodis either aCreditCardOR aPayPalaccount, that is a sum.
Composition in Action
By nesting these, you can model complex systems that are impossible to represent in an "invalid state." Consider an e-commerce order system:
struct CreditCard { number: u64, expiry: String }
struct PayPal { email: String }
// Sum type (The "OR" logic)
enum PaymentMethod {
CreditCard(CreditCard),
PayPal(PayPal),
Cash,
}
// Product type (The "AND" logic)
struct Order {
id: u32,
method: PaymentMethod,
}
In this model, the compiler ensures you cannot accidentally use a PayPal field when the user selected Cash. You have explicitly defined the boundaries of your domain.
Why This Matters
When you model your domain using these primitives, you move the responsibility of checking validity from runtime logic (like if-else chains or null checks) to the compiler. If you add a new payment method, the compiler will force you to update every match statement in your codebase that processes payments.
By combining products and sums, you create a "domain language" where the code structure itself documents the business rules. You aren't just storing data; you are creating a type-level map of what is possible within your application.
Key Takeaway: Use Product types to group related data (AND) and Sum types to define mutually exclusive states (OR); when composed, these patterns make illegal states unrepresentable.