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

Generic Database Mapping Layers

Map complex application structs to database rows using trait-driven generic code generation.

In this lesson, you will learn how to use trait-driven generics to map your application structs to database rows, decoupling your business logic from specific database implementation details.

The Problem: Rigid Database Logic

When building applications, we often end up writing repetitive code to convert database rows (like tuples or key-value maps) into our own Rust structs. If you write this logic manually for every entity, your code becomes brittle. If you change a database schema or switch libraries, you have to rewrite every mapping function.

The Solution: Generic Trait Mapping

Instead of hardcoding the transformation, we define a trait that describes how an entity can be constructed from a database row. By using generics, we can write a single, reusable function that works for any struct that implements our mapping trait.

Consider this pattern:

pub trait FromRow {
    fn from_row(row: &DatabaseRow) -> Self;
}

// A generic function that works for any type T that implements FromRow
pub fn fetch_entity<T: FromRow>(id: i32) -> T {
    let row = database_query("SELECT * FROM table WHERE id = ?", id);
    T::from_row(&row)
}

struct User { id: i32, name: String }

impl FromRow for User {
    fn from_row(row: &DatabaseRow) -> Self {
        User {
            id: row.get("id"),
            name: row.get("name"),
        }
    }
}

Why this is powerful

By using T: FromRow, the fetch_entity function doesn't need to know the internal structure of your User (or Product, or Order). It only needs to know that the type knows how to "hydrate" itself from a generic DatabaseRow.

This approach uses static dispatch, meaning the compiler generates specific code for each type you use. It is highly performant because there is no runtime overhead for checking types—the transformation logic is baked in at compile time. This allows you to build a generic repository layer that handles the "plumbing" of database interaction, while your business structs remain clean, simple, and oblivious to the database driver.

Key Takeaway: By defining a generic FromRow trait, you create a contract that allows your database code to remain agnostic of the specific application data structures it retrieves.