Course contentsShow
← Rust
Lesson 784 of 1,9106. Generics and Static DispatchFree lesson

Type-Driven Input Validation

Use generic validation traits to verify external input structure at compile time.

In this lesson, you will learn how to leverage the Rust type system to guarantee that data is valid by construction, moving validation logic from runtime checks to compile-time guarantees.

The Problem with Runtime Checks

Usually, when we accept raw input (like a string or a JSON object), we perform "defensive programming": we check if a string is empty, if a number is within a range, or if an email address has an "@" symbol. This often leads to scattered if statements throughout your business logic, leaving you constantly wondering if a variable has actually been validated yet.

Moving Logic to the Type System

Instead of passing around raw data, we can define a trait that enforces a validation requirement for any type that represents "checked" data. By using a generic wrapper, we ensure that a function can only accept data that has already passed through a validator.

Consider this pattern:

pub trait Validator<T> {
    fn validate(input: T) -> Result<T, String>;
}

// A generic wrapper that only holds validated data
pub struct Validated<T, V: Validator<T>> {
    data: T,
    _marker: std::marker::PhantomData<V>,
}

impl<T, V: Validator<T>> Validated<T, V> {
    pub fn new(input: T) -> Result<Self, String> {
        let validated_data = V::validate(input)?;
        Ok(Self { data: validated_data, _marker: std::marker::PhantomData })
    }
    
    pub fn into_inner(self) -> T { self.data }
}

By defining a specific Validator struct for a type—for example, an EmailValidator that implements Validator<String>—we can require functions to accept Validated<String, EmailValidator> instead of a plain String.

When you write a function signature like fn send_email(address: Validated<String, EmailValidator>), the compiler becomes your first line of defense. You no longer need to write error handling for an invalid email inside send_email because it is physically impossible to construct that type without passing through the validator. This "Type-Driven" approach eliminates the state of "uncertainty" in your application's data flow.

Key Takeaway: By encoding validation requirements into generic types, you shift error handling from a runtime burden to a structural requirement enforced by the compiler.