How does serde::de::DeserializeSeed enable stateful deserialization with context propagation through the deserialization tree?

DeserializeSeed enables passing external context through every level of deserialization by wrapping the deserialization process with a seed object that carries stateβ€”the seed's deserialize method receives the deserializer and can provide context to nested types through a custom Visitor implementation that holds references to the seed's state, allowing the entire deserialization tree to access shared context like ID maps, type registries, or dependency injectors that regular Deserialize implementations cannot access. This pattern solves the fundamental problem that Deserialize trait methods are staticβ€”they have no access to external stateβ€”while DeserializeSeed implementations can hold and propagate arbitrary context through the visitor-based deserialization process.

The DeserializeSeed Trait Structure

use serde::de::{DeserializeSeed, Deserializer, Visitor, Error};
use std::marker::PhantomData;
 
// The DeserializeSeed trait (simplified):
pub trait DeserializeSeed<'de>: Sized {
    // The type to be produced by deserialization
    type Value;
    
    // Deserialize using the given deserializer
    // The seed can use its internal state during deserialization
    fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
    where
        D: Deserializer<'de>;
}
 
// Compare with regular Deserialize:
pub trait Deserialize<'de>: Sized {
    type Value = Self;  // Always produces Self
    
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>;
}
 
// Key differences:
// β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
// β”‚                     Deserialize vs DeserializeSeed                          β”‚
// β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
// β”‚ Trait              β”‚ Context    β”‚ Value Type    β”‚ Use Case                 β”‚
// β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
// β”‚ Deserialize        β”‚ None       β”‚ Self          β”‚ Simple deserialization   β”‚
// β”‚                    β”‚            β”‚               β”‚ No external state needed β”‚
// β”‚                    β”‚            β”‚               β”‚ Standard implementations β”‚
// β”‚                    β”‚            β”‚               β”‚                          β”‚
// β”‚ DeserializeSeed    β”‚ Can hold   β”‚ Associated    β”‚ Stateful deserialization β”‚
// β”‚                    β”‚ arbitrary  β”‚ type          β”‚ Context propagation      β”‚
// β”‚                    β”‚ state      β”‚               β”‚ Reference resolution     β”‚
// β”‚                    β”‚            β”‚               β”‚ Type registries          β”‚
// β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
 
// The seed can be any type that holds context:
 
struct IdResolvingSeed<'a> {
    id_map: &'a HashMap<u64, String>,  // Context: ID -> Name mapping
}
 
impl<'de> DeserializeSeed<'de> for IdResolvingSeed<'_> {
    type Value = String;  // Produces String, not Self
    
    fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
    where
        D: Deserializer<'de>,
    {
        // Use context during deserialization
        let id: u64 = Deserialize::deserialize(deserializer)?;
        
        // Resolve ID to name using context
        self.id_map.get(&id)
            .cloned()
            .ok_or_else(|| D::Error::custom(format!("Unknown ID: {}", id)))
    }
}

DeserializeSeed adds context capability to deserialization through an associated Value type and internal state access.

Context Propagation Through the Deserialization Tree

use serde::de::{DeserializeSeed, Deserializer, Visitor, Error, SeqAccess, MapAccess};
use serde::Deserialize;
use std::collections::HashMap;
use std::marker::PhantomData;
 
// Problem: Resolving IDs to actual values during deserialization
 
// Example: JSON with IDs that need resolution
// {"user_id": 42, "project_id": 100}
// Needs to become: {"user": "Alice", "project": "Rust Compiler"}
 
// Context: Maps that resolve IDs
struct ResolutionContext {
    users: HashMap<u64, String>,
    projects: HashMap<u64, String>,
}
 
// Seed that propagates context through the tree:
 
struct ContextualSeed<'a, T> {
    context: &'a ResolutionContext,
    _marker: PhantomData<T>,
}
 
impl<'a, T> ContextualSeed<'a, T> {
    fn new(context: &'a ResolutionContext) -> Self {
        Self {
            context,
            _marker: PhantomData,
        }
    }
}
 
// The seed for a struct can propagate to fields:
 
struct DocumentSeed<'a> {
    context: &'a ResolutionContext,
}
 
impl<'de> DeserializeSeed<'de> for DocumentSeed<'_> {
    type Value = Document;
    
    fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
    where
        D: Deserializer<'de>,
    {
        // Create a visitor that has access to the context
        let visitor = DocumentVisitor {
            context: self.context,
        };
        
        deserializer.deserialize_map(visitor)
    }
}
 
struct DocumentVisitor<'a> {
    context: &'a ResolutionContext,
}
 
impl<'de> Visitor<'de> for DocumentVisitor<'_> {
    type Value = Document;
    
    fn visit_map<M>(self, mut map: M) -> Result<Document, M::Error>
    where
        M: MapAccess<'de>,
    {
        let mut user = None;
        let mut project = None;
        
        while let Some(key) = map.next_key::<String>()? {
            match key.as_str() {
                "user_id" => {
                    // Context propagates to field deserialization
                    let id: u64 = map.next_value()?;
                    user = Some(self.context.users.get(&id)
                        .cloned()
                        .ok_or_else(|| M::Error::custom("Unknown user ID"))?);
                }
                "project_id" => {
                    let id: u64 = map.next_value()?;
                    project = Some(self.context.projects.get(&id)
                        .cloned()
                        .ok_or_else(|| M::Error::custom("Unknown project ID"))?);
                }
                _ => {
                    return Err(M::Error::unknown_field(&key, &["user_id", "project_id"]));
                }
            }
        }
        
        Ok(Document {
            user: user.ok_or_else(|| M::Error::missing_field("user_id"))?,
            project: project.ok_or_else(|| M::Error::missing_field("project_id"))?,
        })
    }
}
 
#[derive(Debug)]
struct Document {
    user: String,
    project: String,
}
 
// Usage:
fn deserialize_with_context() {
    let context = ResolutionContext {
        users: vec![(42, "Alice".to_string())].into_iter().collect(),
        projects: vec![(100, "Rust Compiler".to_string())].into_iter().collect(),
    };
    
    let json = r#"{"user_id": 42, "project_id": 100}"#;
    
    let seed = DocumentSeed { context: &context };
    let document: Document = seed.deserialize(&mut serde_json::Deserializer::from_str(json))
        .unwrap();
    
    // document.user == "Alice"
    // document.project == "Rust Compiler"
}

The visitor pattern allows context to flow through the deserialization treeβ€”each nested level can access the seed's context.

Practical Use Case: Type Registry

use serde::de::{DeserializeSeed, Deserializer, Visitor, Error};
use serde::{Deserialize, Deserializer as _};
use std::collections::HashMap;
 
// Use case: Deserializing polymorphic types with a registry
 
trait Message: std::fmt::Debug {
    fn process(&self);
}
 
#[derive(Debug)]
struct PingMessage { sequence: u64 }
 
#[derive(Debug)]
struct PongMessage { sequence: u64 }
 
#[derive(Debug)]
struct DataMessage { data: Vec<u8> }
 
impl Message for PingMessage {
    fn process(&self) { println!("Ping: {}", self.sequence); }
}
 
impl Message for PongMessage {
    fn process(&self) { println!("Pong: {}", self.sequence); }
}
 
impl Message for DataMessage {
    fn process(&self) { println!("Data: {} bytes", self.data.len()); }
}
 
// Registry that maps type names to deserializers
type MessageFactory = Box<dyn Fn(&mut dyn Deserializer<'_>) -> Result<Box<dyn Message>, serde_json::Error> + Send + Sync>;
 
struct MessageRegistry {
    factories: HashMap<String, MessageFactory>,
}
 
impl MessageRegistry {
    fn new() -> Self {
        Self { factories: HashMap::new() }
    }
    
    fn register<M>(&mut self, type_name: &str)
    where
        M: Message + for<'de> Deserialize<'de> + 'static,
    {
        self.factories.insert(
            type_name.to_string(),
            Box::new(|de| {
                let msg: M = M::deserialize(de)?;
                Ok(Box::new(msg) as Box<dyn Message>)
            }),
        );
    }
    
    fn deserialize(&self, type_name: &str, de: &mut dyn Deserializer<'_>) -> Result<Box<dyn Message>, serde_json::Error> {
        self.factories.get(type_name)
            .ok_or_else(|| serde::de::Error::custom(format!("Unknown message type: {}", type_name)))
            .and_then(|factory| factory(de))
    }
}
 
// Seed that uses the registry to deserialize polymorphic messages
 
struct MessageSeed<'a> {
    registry: &'a MessageRegistry,
}
 
impl<'de> DeserializeSeed<'de> for MessageSeed<'_> {
    type Value = Box<dyn Message>;
    
    fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
    where
        D: Deserializer<'de>,
    {
        // The visitor receives the registry and uses it during deserialization
        let visitor = MessageVisitor {
            registry: self.registry,
        };
        
        deserializer.deserialize_map(visitor)
    }
}
 
struct MessageVisitor<'a> {
    registry: &'a MessageRegistry,
}
 
impl<'de> Visitor<'de> for MessageVisitor<'_> {
    type Value = Box<dyn Message>;
    
    fn visit_map<M>(self, mut map: M) -> Result<Box<dyn Message>, M::Error>
    where
        M: serde::de::MapAccess<'de>,
    {
        let mut type_name = None;
        let mut payload = None;
        
        while let Some(key) = map.next_key::<String>()? {
            match key.as_str() {
                "type" => {
                    type_name = Some(map.next_value()?);
                }
                "payload" => {
                    // Store raw JSON value for later deserialization
                    payload = Some(map.next_value::<serde_json::Value>()?);
                }
                _ => {
                    return Err(M::Error::unknown_field(&key, &["type", "payload"]));
                }
            }
        }
        
        let type_name: String = type_name.ok_or_else(|| M::Error::missing_field("type"))?;
        let payload: serde_json::Value = payload.ok_or_else(|| M::Error::missing_field("payload"))?;
        
        // Use registry to deserialize the payload based on type
        // This requires a bit of indirection since we need to deserialize the payload
        self.registry.deserialize(&type_name, &mut serde_json::Deserializer::from_str(&payload.to_string()))
            .map_err(M::Error::custom)
    }
}
 
// Usage:
fn type_registry_example() {
    let mut registry = MessageRegistry::new();
    registry.register::<PingMessage>("ping");
    registry.register::<PongMessage>("pong");
    registry.register::<DataMessage>("data");
    
    let json = r#"{"type": "ping", "payload": {"sequence": 42}}"#;
    
    let seed = MessageSeed { registry: &registry };
    let message: Box<dyn Message> = seed
        .deserialize(&mut serde_json::Deserializer::from_str(json))
        .unwrap();
    
    message.process();  // Prints "Ping: 42"
}

The registry acts as context, allowing polymorphic deserialization where the concrete type is determined by data in the JSON itself.

Nested Context Propagation

use serde::de::{DeserializeSeed, Deserializer, Visitor, SeqAccess};
use serde::Deserialize;
use std::collections::HashMap;
 
// Example: Nested structures with shared context
 
#[derive(Debug)]
struct Order {
    id: u64,
    customer: Customer,
    items: Vec<Item>,
}
 
#[derive(Debug)]
struct Customer {
    id: u64,
    name: String,  // Resolved from context
}
 
#[derive(Debug)]
struct Item {
    product_id: u64,
    product_name: String,  // Resolved from context
    quantity: u32,
}
 
struct OrderContext {
    customer_names: HashMap<u64, String>,
    product_names: HashMap<u64, String>,
}
 
// Seed for Order (top-level)
 
struct OrderSeed<'a> {
    context: &'a OrderContext,
}
 
impl<'de> DeserializeSeed<'de> for OrderSeed<'_> {
    type Value = Order;
    
    fn deserialize<D>(self, deserializer: D) -> Result<Order, D::Error>
    where
        D: Deserializer<'de>,
    {
        deserializer.deserialize_map(OrderVisitor { context: self.context })
    }
}
 
struct OrderVisitor<'a> {
    context: &'a OrderContext,
}
 
impl<'de> Visitor<'de> for OrderVisitor<'_> {
    type Value = Order;
    
    fn visit_map<M>(self, mut map: M) -> Result<Order, M::Error>
    where
        M: serde::de::MapAccess<'de>,
    {
        let mut id = None;
        let mut customer = None;
        let mut items = None;
        
        while let Some(key) = map.next_key::<String>()? {
            match key.as_str() {
                "id" => id = Some(map.next_value()?),
                "customer" => {
                    // Context propagates to nested CustomerSeed
                    let seed = CustomerSeed { context: self.context };
                    customer = Some(map.next_value_seed(seed)?);
                }
                "items" => {
                    // Context propagates to nested ItemSeed collection
                    let seed = ItemsSeed { context: self.context };
                    items = Some(map.next_value_seed(seed)?);
                }
                _ => { /* ignore unknown fields */ }
            }
        }
        
        Ok(Order {
            id: id.ok_or_else(|| M::Error::missing_field("id"))?,
            customer: customer.ok_or_else(|| M::Error::missing_field("customer"))?,
            items: items.ok_or_else(|| M::Error::missing_field("items"))?,
        })
    }
}
 
// Seed for Customer (nested)
 
struct CustomerSeed<'a> {
    context: &'a OrderContext,
}
 
impl<'de> DeserializeSeed<'de> for CustomerSeed<'_> {
    type Value = Customer;
    
    fn deserialize<D>(self, deserializer: D) -> Result<Customer, D::Error>
    where
        D: Deserializer<'de>,
    {
        deserializer.deserialize_map(CustomerVisitor { context: self.context })
    }
}
 
struct CustomerVisitor<'a> {
    context: &'a OrderContext,
}
 
impl<'de> Visitor<'de> for CustomerVisitor<'_> {
    type Value = Customer;
    
    fn visit_map<M>(self, mut map: M) -> Result<Customer, M::Error>
    where
        M: serde::de::MapAccess<'de>,
    {
        let mut id = None;
        
        while let Some(key) = map.next_key::<String>()? {
            match key.as_str() {
                "id" => id = Some(map.next_value()?),
                _ => { /* ignore */ }
            }
        }
        
        let id = id.ok_or_else(|| M::Error::missing_field("id"))?;
        let name = self.context.customer_names.get(&id)
            .cloned()
            .ok_or_else(|| M::Error::custom(format!("Unknown customer: {}", id)))?;
        
        Ok(Customer { id, name })
    }
}
 
// Seed for Items collection
 
struct ItemsSeed<'a> {
    context: &'a OrderContext,
}
 
impl<'de> DeserializeSeed<'de> for ItemsSeed<'_> {
    type Value = Vec<Item>;
    
    fn deserialize<D>(self, deserializer: D) -> Result<Vec<Item>, D::Error>
    where
        D: Deserializer<'de>,
    {
        deserializer.deserialize_seq(ItemsVisitor { context: self.context })
    }
}
 
struct ItemsVisitor<'a> {
    context: &'a OrderContext,
}
 
impl<'de> Visitor<'de> for ItemsVisitor<'_> {
    type Value = Vec<Item>;
    
    fn visit_seq<M>(self, mut seq: M) -> Result<Vec<Item>, M::Error>
    where
        M: SeqAccess<'de>,
    {
        let mut items = Vec::new();
        
        // Each item uses a seed with context
        let item_seed = ItemSeed { context: self.context };
        
        while let Some(item) = seq.next_element_seed(item_seed)? {
            items.push(item);
        }
        
        Ok(items)
    }
}
 
// Note: SeqAccess::next_element_seed takes a seed, not a type
// This is how context propagates into collections
 
struct ItemSeed<'a> {
    context: &'a OrderContext,
}
 
impl<'de> DeserializeSeed<'de> for ItemSeed<'_> {
    type Value = Item;
    
    fn deserialize<D>(self, deserializer: D) -> Result<Item, D::Error>
    where
        D: Deserializer<'de>,
    {
        deserializer.deserialize_map(ItemVisitor { context: self.context })
    }
}
 
struct ItemVisitor<'a> {
    context: &'a OrderContext,
}
 
impl<'de> Visitor<'de> for ItemVisitor<'_> {
    type Value = Item;
    
    fn visit_map<M>(self, mut map: M) -> Result<Item, M::Error>
    where
        M: serde::de::MapAccess<'de>,
    {
        let mut product_id = None;
        let mut quantity = None;
        
        while let Some(key) = map.next_key::<String>()? {
            match key.as_str() {
                "product_id" => product_id = Some(map.next_value()?),
                "quantity" => quantity = Some(map.next_value()?),
                _ => { /* ignore */ }
            }
        }
        
        let product_id = product_id.ok_or_else(|| M::Error::missing_field("product_id"))?;
        let quantity = quantity.ok_or_else(|| M::Error::missing_field("quantity"))?;
        
        let product_name = self.context.product_names.get(&product_id)
            .cloned()
            .ok_or_else(|| M::Error::custom(format!("Unknown product: {}", product_id)))?;
        
        Ok(Item { product_id, product_name, quantity })
    }
}
 
// The context flows through the entire deserialization tree:
// OrderSeed -> OrderVisitor -> CustomerSeed -> CustomerVisitor
//                        \-> ItemsSeed -> ItemsVisitor -> ItemSeed -> ItemVisitor

Context propagates from parent seeds to child seeds through the visitor pattern, reaching every level of the deserialization tree.

Comparing DeserializeSeed Patterns

use serde::de::{DeserializeSeed, Deserializer, Visitor};
use serde::Deserialize;
use std::collections::HashMap;
 
// Pattern 1: Closure-based seed (ad-hoc context)
 
struct ClosureSeed<F, T> {
    f: F,
    _marker: std::marker::PhantomData<T>,
}
 
impl<'de, F, T> DeserializeSeed<'de> for ClosureSeed<F, T>
where
    F: FnOnce(T) -> Result<T, serde_json::Error>,
{
    type Value = T;
    
    fn deserialize<D>(self, deserializer: D) -> Result<T, D::Error>
    where
        D: Deserializer<'de>,
    {
        let value: T = T::deserialize(deserializer)?;
        (self.f)(value).map_err(serde::de::Error::custom)
    }
}
 
// Pattern 2: Reference seed (borrowed context)
 
struct RefSeed<'a, C, T, F> {
    context: &'a C,
    transform: F,
    _marker: std::marker::PhantomData<T>,
}
 
impl<'de, 'a, C, T, F> DeserializeSeed<'de> for RefSeed<'a, C, T, F>
where
    F: FnOnce(T, &C) -> Result<T, serde_json::Error>,
{
    type Value = T;
    
    fn deserialize<D>(self, deserializer: D) -> Result<T, D::Error>
    where
        D: Deserializer<'de>,
    {
        let value: T = T::deserialize(deserializer)?;
        (self.transform)(value, self.context).map_err(serde::de::Error::custom)
    }
}
 
// Pattern 3: Owned context seed
 
struct OwnedContextSeed<C, T, F> {
    context: C,
    transform: F,
    _marker: std::marker::PhantomData<T>,
}
 
impl<'de, C, T, F> DeserializeSeed<'de> for OwnedContextSeed<C, T, F>
where
    F: FnOnce(T, C) -> Result<T, serde_json::Error>,
{
    type Value = T;
    
    fn deserialize<D>(self, deserializer: D) -> Result<T, D::Error>
    where
        D: Deserializer<'de>,
    {
        let value: T = T::deserialize(deserializer)?;
        (self.transform)(value, self.context).map_err(serde::de::Error::custom)
    }
}
 
// Pattern 4: Transform seed (post-processing)
 
struct TransformSeed<T, U, F> {
    inner: T,  // Inner seed
    transform: F,
    _marker: std::marker::PhantomData<U>,
}
 
impl<'de, T, U, F> DeserializeSeed<'de> for TransformSeed<T, U, F>
where
    T: DeserializeSeed<'de>,
    F: FnOnce(T::Value) -> Result<U, serde_json::Error>,
{
    type Value = U;
    
    fn deserialize<D>(self, deserializer: D) -> Result<U, D::Error>
    where
        D: Deserializer<'de>,
    {
        let intermediate = self.inner.deserialize(deserializer)?;
        (self.transform)(intermediate).map_err(serde::de::Error::custom)
    }
}
 
// Usage comparison:
 
fn pattern_comparison() {
    let context = HashMap::from([(1u64, "Alice".to_string())]);
    
    // Pattern 1: Closure (no context needed, just transformation)
    let seed = ClosureSeed {
        f: |id: u64| Ok(id * 2),
        _marker: std::marker::PhantomData,
    };
    
    // Pattern 2: Reference (borrowed context)
    let seed = RefSeed {
        context: &context,
        transform: |id: u64, ctx| {
            ctx.get(&id)
                .map(|s| s.clone())
                .ok_or_else(|| format!("Unknown ID: {}", id).into())
        },
        _marker: std::marker::PhantomData,
    };
    
    // Pattern 3: Owned context (context consumed)
    let seed = OwnedContextSeed {
        context: context.clone(),
        transform: |id: u64, ctx| {
            ctx.get(&id)
                .map(|s| s.clone())
                .ok_or_else(|| format!("Unknown ID: {}", id).into())
        },
        _marker: std::marker::PhantomData,
    };
    
    // Pattern 4: Transform (composing seeds)
    let inner_seed = RefSeed {
        context: &context,
        transform: |id: u64, ctx| Ok(ctx.get(&id).unwrap().clone()),
        _marker: std::marker::PhantomData,
    };
    
    let seed = TransformSeed {
        inner: inner_seed,
        transform: |name: String| Ok(format!("Hello, {}!", name)),
        _marker: std::marker::PhantomData,
    };
}

Different seed patterns handle context in different waysβ€”borrowed references, owned values, closures, and transformations.

Using next_value_seed for Nested Propagation

use serde::de::{DeserializeSeed, Deserializer, Visitor, MapAccess, SeqAccess};
use serde::Deserialize;
 
// The key to propagating context through structures:
// MapAccess::next_value_seed and SeqAccess::next_element_seed
 
struct Database {
    users: Vec<User>,
    posts: Vec<Post>,
}
 
struct User {
    id: u64,
    name: String,
}
 
struct Post {
    id: u64,
    author_id: u64,  // References a user
    author_name: String,  // Resolved from context
    title: String,
}
 
// Context maintains user lookup
struct DbContext {
    users: HashMap<u64, String>,
}
 
// Top-level database seed
 
struct DatabaseSeed<'a> {
    context: &'a mut DbContext,
}
 
impl<'de> DeserializeSeed<'de> for DatabaseSeed<'_> {
    type Value = Database;
    
    fn deserialize<D>(self, deserializer: D) -> Result<Database, D::Error>
    where
        D: Deserializer<'de>,
    {
        deserializer.deserialize_map(DatabaseVisitor { context: self.context })
    }
}
 
struct DatabaseVisitor<'a> {
    context: &'a mut DbContext,
}
 
impl<'de> Visitor<'de> for DatabaseVisitor<'_> {
    type Value = Database;
    
    fn visit_map<M>(self, mut map: M) -> Result<Database, M::Error>
    where
        M: MapAccess<'de>,
    {
        let mut users = None;
        let mut posts = None;
        
        while let Some(key) = map.next_key::<String>()? {
            match key.as_str() {
                "users" => {
                    // Deserialize users first to populate context
                    users = Some(map.next_value::<Vec<User>>()?);
                    
                    // Populate context with user names
                    for user in users.as_ref().unwrap() {
                        self.context.users.insert(user.id, user.name.clone());
                    }
                }
                "posts" => {
                    // Posts need context to resolve author names
                    let seed = PostsSeed { context: self.context };
                    posts = Some(map.next_value_seed(seed)?);
                }
                _ => { /* ignore */ }
            }
        }
        
        Ok(Database {
            users: users.ok_or_else(|| M::Error::missing_field("users"))?,
            posts: posts.ok_or_else(|| M::Error::missing_field("posts"))?,
        })
    }
}
 
// Posts collection seed
 
struct PostsSeed<'a> {
    context: &'a DbContext,
}
 
impl<'de> DeserializeSeed<'de> for PostsSeed<'_> {
    type Value = Vec<Post>;
    
    fn deserialize<D>(self, deserializer: D) -> Result<Vec<Post>, D::Error>
    where
        D: Deserializer<'de>,
    {
        deserializer.deserialize_seq(PostsVisitor { context: self.context })
    }
}
 
struct PostsVisitor<'a> {
    context: &'a DbContext,
}
 
impl<'de> Visitor<'de> for PostsVisitor<'_> {
    type Value = Vec<Post>;
    
    fn visit_seq<M>(self, mut seq: M) -> Result<Vec<Post>, M::Error>
    where
        M: SeqAccess<'de>,
    {
        let mut posts = Vec::new();
        
        // Key insight: next_element_seed propagates context to each element
        let post_seed = PostSeed { context: self.context };
        
        while let Some(post) = seq.next_element_seed(post_seed)? {
            posts.push(post);
        }
        
        Ok(posts)
    }
}
 
// Individual post seed
 
struct PostSeed<'a> {
    context: &'a DbContext,
}
 
impl<'de> DeserializeSeed<'de> for PostSeed<'_> {
    type Value = Post;
    
    fn deserialize<D>(self, deserializer: D) -> Result<Post, D::Error>
    where
        D: Deserializer<'de>,
    {
        deserializer.deserialize_map(PostVisitor { context: self.context })
    }
}
 
struct PostVisitor<'a> {
    context: &'a DbContext,
}
 
impl<'de> Visitor<'de> for PostVisitor<'_> {
    type Value = Post;
    
    fn visit_map<M>(self, mut map: M) -> Result<Post, M::Error>
    where
        M: MapAccess<'de>,
    {
        let mut id = None;
        let mut author_id = None;
        let mut title = None;
        
        while let Some(key) = map.next_key::<String>()? {
            match key.as_str() {
                "id" => id = Some(map.next_value()?),
                "author_id" => author_id = Some(map.next_value()?),
                "title" => title = Some(map.next_value()?),
                _ => { /* ignore */ }
            }
        }
        
        let author_id = author_id.ok_or_else(|| M::Error::missing_field("author_id"))?;
        
        // Resolve author_name using context
        let author_name = self.context.users.get(&author_id)
            .cloned()
            .ok_or_else(|| M::Error::custom(format!("Unknown author: {}", author_id)))?;
        
        Ok(Post {
            id: id.ok_or_else(|| M::Error::missing_field("id"))?,
            author_id,
            author_name,
            title: title.ok_or_else(|| M::Error::missing_field("title"))?,
        })
    }
}
 
// The order matters: users must be deserialized before posts
// so the context has the user map populated
 
// Usage:
fn database_example() {
    let json = r#"{
        "users": [
            {"id": 1, "name": "Alice"},
            {"id": 2, "name": "Bob"}
        ],
        "posts": [
            {"id": 1, "author_id": 1, "title": "Hello"},
            {"id": 2, "author_id": 2, "title": "World"}
        ]
    }"#;
    
    let mut context = DbContext {
        users: HashMap::new(),
    };
    
    let seed = DatabaseSeed { context: &mut context };
    let db: Database = seed.deserialize(&mut serde_json::Deserializer::from_str(json)).unwrap();
    
    // Posts now have author_name resolved
    assert_eq!(db.posts[0].author_name, "Alice");
    assert_eq!(db.posts[1].author_name, "Bob");
}

next_value_seed and next_element_seed are the mechanism that propagates seeds through structuresβ€”they allow passing context-aware seeds to nested values.

DeserializeSeed for Dependency Injection

use serde::de::{DeserializeSeed, Deserializer, Visitor, MapAccess};
use serde::Deserialize;
use std::sync::Arc;
 
// Use case: Injecting services during deserialization
 
trait DataService: std::fmt::Debug {
    fn get_user(&self, id: u64) -> Option<String>;
}
 
#[derive(Debug)]
struct MockDataService {
    users: HashMap<u64, String>,
}
 
impl DataService for MockDataService {
    fn get_user(&self, id: u64) -> Option<String> {
        self.users.get(&id).cloned()
    }
}
 
struct Request {
    user_id: u64,
    user_name: String,  // Injected from service
    action: String,
}
 
struct RequestSeed {
    service: Arc<dyn DataService>,
}
 
impl<'de> DeserializeSeed<'de> for RequestSeed {
    type Value = Request;
    
    fn deserialize<D>(self, deserializer: D) -> Result<Request, D::Error>
    where
        D: Deserializer<'de>,
    {
        deserializer.deserialize_map(RequestVisitor { service: self.service })
    }
}
 
struct RequestVisitor {
    service: Arc<dyn DataService>,
}
 
impl<'de> Visitor<'de> for RequestVisitor {
    type Value = Request;
    
    fn visit_map<M>(self, mut map: M) -> Result<Request, M::Error>
    where
        M: MapAccess<'de>,
    {
        let mut user_id = None;
        let mut action = None;
        
        while let Some(key) = map.next_key::<String>()? {
            match key.as_str() {
                "user_id" => user_id = Some(map.next_value()?),
                "action" => action = Some(map.next_value()?),
                _ => { /* ignore */ }
            }
        }
        
        let user_id = user_id.ok_or_else(|| M::Error::missing_field("user_id"))?;
        let action = action.ok_or_else(|| M::Error::missing_field("action"))?;
        
        // Inject user_name from service
        let user_name = self.service.get_user(user_id)
            .ok_or_else(|| M::Error::custom(format!("Unknown user: {}", user_id)))?;
        
        Ok(Request { user_id, user_name, action })
    }
}
 
// Usage with dependency injection:
 
fn dependency_injection_example() {
    let service = Arc::new(MockDataService {
        users: vec![(1, "Alice".to_string()), (2, "Bob".to_string())].into_iter().collect(),
    });
    
    let json = r#"{"user_id": 1, "action": "login"}"#;
    
    let seed = RequestSeed { service: service.clone() };
    let request: Request = seed.deserialize(&mut serde_json::Deserializer::from_str(json)).unwrap();
    
    // request.user_name == "Alice" (injected from service)
    assert_eq!(request.user_name, "Alice");
}
 
// This pattern enables:
// - Injecting databases, APIs, or other services
// - Deserializing objects that depend on runtime state
// - Building rich domain objects from serialized IDs
// - Keeping deserialization decoupled from infrastructure

Dependency injection through DeserializeSeed allows deserialization to access external services and resources.

Comparison with Alternative Approaches

use serde::Deserialize;
use std::collections::HashMap;
 
// Alternative 1: Post-deserialization resolution
 
#[derive(Deserialize)]
struct RequestRaw {
    user_id: u64,
    action: String,
}
 
impl RequestRaw {
    fn resolve(self, users: &HashMap<u64, String>) -> Result<Request, String> {
        let user_name = users.get(&self.user_id)
            .cloned()
            .ok_or_else(|| format!("Unknown user: {}", self.user_id))?;
        
        Ok(Request {
            user_id: self.user_id,
            user_name,
            action: self.action,
        })
    }
}
 
// Pros: Simple, no custom visitors
// Cons: Two-phase process, must remember to resolve
 
// Alternative 2: Context field in struct
 
#[derive(Deserialize)]
struct RequestWithContext {
    user_id: u64,
    #[serde(skip)]
    user_name: Option<String>,
    action: String,
}
 
impl RequestWithContext {
    fn resolve(&mut self, users: &HashMap<u64, String>) -> Result<(), String> {
        self.user_name = Some(users.get(&self.user_id)
            .cloned()
            .ok_or_else(|| format!("Unknown user: {}", self.user_id))?);
        Ok(())
    }
}
 
// Pros: Can derive Deserialize
// Cons: Mutable after deserialization, skip fields
 
// Alternative 3: Custom deserialize_with attribute
 
fn deserialize_user_name<'de, D>(deserializer: D) -> Result<String, D::Error>
where
    D: serde::Deserializer<'de>,
{
    // Problem: No access to context!
    // Can only use static data or thread-local storage
    let id: u64 = u64::deserialize(deserializer)?;
    
    // Would need thread-local to access context:
    thread_local! {
        static USER_MAP: std::cell::RefCell<HashMap<u64, String>> = Default::default();
    }
    
    USER_MAP.with(|map| {
        map.borrow().get(&id)
            .cloned()
            .ok_or_else(|| serde::de::Error::custom(format!("Unknown user: {}", id)))
    })
}
 
// Pros: Works with derive macros
// Cons: Requires thread-local, not thread-safe
 
// Alternative 4: DeserializeSeed (our approach)
 
// Pros:
// - Context explicitly passed through
// - Thread-safe
// - No hidden state
// - Composable through visitor pattern
 
// Cons:
// - Custom visitor implementations needed
// - More verbose
// - Cannot use derive macros
 
// β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
// β”‚                    APPROACH COMPARISON                                      β”‚
// β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
// β”‚ Approach          β”‚ Complexity β”‚ Thread-safe β”‚ Composable β”‚ Derive?      β”‚
// β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
// β”‚ Post-deserialize  β”‚ Low        β”‚ Yes         β”‚ No          β”‚ Yes          β”‚
// β”‚ Skip field        β”‚ Low        β”‚ Yes         β”‚ No          β”‚ Yes          β”‚
// β”‚ Thread-local      β”‚ Medium     β”‚ No          β”‚ No          β”‚ Yes          β”‚
// β”‚ DeserializeSeed   β”‚ High       β”‚ Yes         β”‚ Yes         β”‚ No           β”‚
// β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

DeserializeSeed is the most complex but offers thread-safety, composability, and explicit context management.

Real-World Patterns

use serde::de::{DeserializeSeed, Deserializer, Visitor, MapAccess};
use serde::Deserialize;
use std::collections::HashMap;
use std::path::PathBuf;
 
// Pattern 1: Path resolution with base directory
 
struct PathContext {
    base_dir: PathBuf,
}
 
struct ResolvedPathSeed<'a> {
    context: &'a PathContext,
}
 
impl<'de> DeserializeSeed<'de> for ResolvedPathSeed<'_> {
    type Value = PathBuf;
    
    fn deserialize<D>(self, deserializer: D) -> Result<PathBuf, D::Error>
    where
        D: Deserializer<'de>,
    {
        let relative: String = String::deserialize(deserializer)?;
        Ok(self.context.base_dir.join(relative))
    }
}
 
// Pattern 2: Environment variable interpolation
 
struct EnvContext {
    env: HashMap<String, String>,
}
 
struct EnvInterpolatingSeed<'a> {
    context: &'a EnvContext,
}
 
impl<'de> DeserializeSeed<'de> for EnvInterpolatingSeed<'_> {
    type Value = String;
    
    fn deserialize<D>(self, deserializer: D) -> Result<String, D::Error>
    where
        D: Deserializer<'de>,
    {
        let template: String = String::deserialize(deserializer)?;
        
        // Interpolate ${VAR} patterns
        let mut result = template;
        for (key, value) in &self.context.env {
            result = result.replace(&format!("${{{}}}", key), value);
        }
        
        Ok(result)
    }
}
 
// Pattern 3: Reference resolution in graphs
 
struct GraphContext {
    nodes: HashMap<String, usize>,  // node_id -> index
}
 
struct EdgeSeed<'a> {
    context: &'a GraphContext,
}
 
#[derive(Debug)]
struct Edge {
    from: usize,  // Resolved index
    to: usize,    // Resolved index
    weight: f64,
}
 
impl<'de> DeserializeSeed<'de> for EdgeSeed<'_> {
    type Value = Edge;
    
    fn deserialize<D>(self, deserializer: D) -> Result<Edge, D::Error>
    where
        D: Deserializer<'de>,
    {
        deserializer.deserialize_map(EdgeVisitor { context: self.context })
    }
}
 
struct EdgeVisitor<'a> {
    context: &'a GraphContext,
}
 
impl<'de> Visitor<'de> for EdgeVisitor<'_> {
    type Value = Edge;
    
    fn visit_map<M>(self, mut map: M) -> Result<Edge, M::Error>
    where
        M: MapAccess<'de>,
    {
        let mut from_id = None;
        let mut to_id = None;
        let mut weight = None;
        
        while let Some(key) = map.next_key::<String>()? {
            match key.as_str() {
                "from" => from_id = Some(map.next_value::<String>()?),
                "to" => to_id = Some(map.next_value::<String>()?),
                "weight" => weight = Some(map.next_value()?),
                _ => { /* ignore */ }
            }
        }
        
        let from_id = from_id.ok_or_else(|| M::Error::missing_field("from"))?;
        let to_id = to_id.ok_or_else(|| M::Error::missing_field("to"))?;
        let weight = weight.ok_or_else(|| M::Error::missing_field("weight"))?;
        
        let from = self.context.nodes.get(&from_id)
            .copied()
            .ok_or_else(|| M::Error::custom(format!("Unknown node: {}", from_id)))?;
        
        let to = self.context.nodes.get(&to_id)
            .copied()
            .ok_or_else(|| M::Error::custom(format!("Unknown node: {}", to_id)))?;
        
        Ok(Edge { from, to, weight })
    }
}
 
// Pattern 4: Validation with external state
 
struct ValidationContext {
    allowed_values: HashMap<String, Vec<String>>,
}
 
struct ValidatedFieldSeed<'a> {
    context: &'a ValidationContext,
    field_name: &'a str,
}
 
impl<'de> DeserializeSeed<'de> for ValidatedFieldSeed<'_> {
    type Value = String;
    
    fn deserialize<D>(self, deserializer: D) -> Result<String, D::Error>
    where
        D: Deserializer<'de>,
    {
        let value: String = String::deserialize(deserializer)?;
        
        if let Some(allowed) = self.context.allowed_values.get(self.field_name) {
            if !allowed.contains(&value) {
                return Err(D::Error::custom(format!(
                    "Invalid value '{}' for field '{}'. Allowed: {:?}",
                    value, self.field_name, allowed
                )));
            }
        }
        
        Ok(value)
    }
}

Real-world patterns show DeserializeSeed enabling path resolution, environment interpolation, graph references, and validation against external state.

Summary

fn summary() {
    // β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    // β”‚                   DESERIALIZESEED SUMMARY                                    β”‚
    // β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
    // 
    // === WHAT IT IS ===
    // 
    // DeserializeSeed is a trait that enables stateful deserialization:
    // - Holds context/state during deserialization
    // - Produces a Value type (not necessarily Self)
    // - Propagates context through the deserialization tree
    // - Works with the Visitor pattern
    // 
    // === HOW IT WORKS ===
    // 
    // 1. Create a seed type holding context
    // 2. Implement DeserializeSeed for the seed
    // 3. Create a visitor that receives the context
    // 4. Use next_value_seed / next_element_seed for nested propagation
    // 
    // === CONTEXT PROPAGATION PATH ===
    // 
    // Seed.deserialize(Deserializer)
    //   └─> creates Visitor with context
    //        └─> Deserializer calls Visitor.visit_*
    //             └─> Visitor uses context during visit
    //                  └─> For nested: create child seed with same context
    //                       └─> call next_value_seed(child_seed)
    // 
    // === KEY METHODS ===
    // 
    // For propagating seeds through structures:
    // 
    // MapAccess::next_value_seed(seed) - for map values
    // SeqAccess::next_element_seed(seed) - for sequence elements
    // 
    // These accept any DeserializeSeed, allowing context to flow
    // through every level of nested structures.
    // 
    // === WHEN TO USE ===
    // 
    // Use DeserializeSeed when:
    // - Resolving IDs to actual values
    // - Injecting dependencies/services
    // - Type registries for polymorphic deserialization
    // - Path resolution with base directories
    // - Validation against external state
    // - Reference resolution in graphs
    // 
    // Use regular Deserialize when:
    // - No external context needed
    // - Simple field mapping
    // - Derive macro sufficient
    // - No cross-field dependencies
    // 
    // === COMPARISON ===
    // 
    // Deserialize:
    // - Static, no state
    // - Derive macro works
    // - Always produces Self
    // - No context access
    // 
    // DeserializeSeed:
    // - Stateful, holds context
    // - Manual implementation required
    // - Can produce any Value type
    // - Full context access
    // 
    // === IMPLEMENTATION PATTERN ===
    // 
    // struct MySeed<'a> {
    //     context: &'a MyContext,
    // }
    // 
    // impl<'de> DeserializeSeed<'de> for MySeed<'_> {
    //     type Value = MyType;
    //     
    //     fn deserialize<D>(self, deserializer: D) -> Result<MyType, D::Error>
    //     where D: Deserializer<'de>
    //     {
    //         deserializer.deserialize_whatever(MyVisitor {
    //             context: self.context,
    //         })
    //     }
    // }
    // 
    // struct MyVisitor<'a> {
    //     context: &'a MyContext,
    // }
    // 
    // impl<'de> Visitor<'de> for MyVisitor<'_> {
    //     type Value = MyType;
    //     
    //     fn visit_*(self, ...) -> Result<MyType, E> {
    //         // Use self.context during deserialization
    //         // Create child seeds for nested values:
    //         // let child_seed = ChildSeed { context: self.context };
    //         // map.next_value_seed(child_seed)?
    //     }
    // }
    // 
    // === KEY INSIGHT ===
    // 
    // DeserializeSeed solves the problem that Deserialize trait methods
    // have no access to external state. By wrapping deserialization
    // in a seed type that holds context, and propagating that context
    // through visitors, every level of the deserialization tree can
    // access shared state for resolution, injection, and validation.
    // 
    // The seed IS the context carrier, the visitor IS the context user,
    // and next_value_seed/next_element_seed ARE the propagation mechanism.
}

Key insight: DeserializeSeed enables stateful deserialization by carrying context through a seed type that creates context-aware visitorsβ€”the pattern propagates context through next_value_seed and next_element_seed calls, allowing every level of the deserialization tree to access external state for ID resolution, dependency injection, type registries, and validation without resorting to thread-local storage or post-deserialization processing.