How does serde::ser::Impossible type serve as a helper for implementing partial serializers?

serde::ser::Impossible is a helper type that implements the Serializer trait with every method returning an error, allowing you to implement only the serialization methods you need while inheriting error implementations for the rest. When building a partial serializer that only handles specific data types, Impossible saves you from writing dozens of stub methods that just return "unsupported" errors—you override only the methods your serializer supports.

The Problem: Implementing Serializer Trait

use serde::Serializer;
use serde::ser::{SerializeSeq, SerializeTuple, SerializeTupleStruct};
// ... many more traits
 
// The Serializer trait has many required methods
// Implementing all of them is tedious when you only need a few
struct MySerializer;
 
impl Serializer for MySerializer {
    // Must implement ALL these methods:
    type Ok = ();
    type Error = serde_json::Error;
    type SerializeSeq = SerializeSeqImpossible;
    type SerializeTuple = SerializeTupleImpossible;
    // ... 20+ more associated types and methods
    
    fn serialize_bool(self, v: bool) -> Result<Self::Ok, Self::Error> {
        // Your implementation
        todo!()
    }
    
    fn serialize_i8(self, v: i8) -> Result<Self::Ok, Self::Error> { todo!() }
    fn serialize_i16(self, v: i16) -> Result<Self::Ok, Self::Error> { todo!() }
    fn serialize_i32(self, v: i32) -> Result<Self::Ok, Self::Error> { todo!() }
    // ... and many more
}

The Serializer trait requires implementing many methods.

Impossible: The Empty Serializer

use serde::ser::Impossible;
 
// Impossible implements Serializer with all methods returning errors
// It's a "default" implementation for partial serializers
 
fn demonstrate_impossible() {
    // Impossible<C, E> takes Ok type and Error type as generics
    let impossible: Impossible<(), serde_json::Error> = Impossible::new("cannot serialize");
    
    // Every method returns an error
    let result = impossible.serialize_bool(true);
    assert!(result.is_err());
}

Impossible is a complete Serializer implementation where everything fails.

Using Impossible for Partial Serializers

use serde::{Serializer, Serialize};
use serde::ser::Impossible;
 
// A serializer that ONLY handles strings
struct StringOnlySerializer {
    output: String,
}
 
impl Serializer for StringOnlySerializer {
    type Ok = String;
    type Error = serde_json::Error;
    
    // Define the Impossible types for sequences, maps, etc.
    type SerializeSeq = Impossible<String, serde_json::Error>;
    type SerializeTuple = Impossible<String, serde_json::Error>;
    type SerializeTupleStruct = Impossible<String, serde_json::Error>;
    type SerializeTupleVariant = Impossible<String, serde_json::Error>;
    type SerializeMap = Impossible<String, serde_json::Error>;
    type SerializeStruct = Impossible<String, serde_json::Error>;
    type SerializeStructVariant = Impossible<String, serde_json::Error>;
    
    // Only implement what you support - strings
    fn serialize_str(mut self, v: &str) -> Result<Self::Ok, Self::Error> {
        self.output.push_str(v);
        Ok(self.output)
    }
    
    // All other methods inherit from Impossible (which errors)
    // You don't have to write them!
}
 
// Now you can serialize strings only
fn serialize_string_only() {
    let serializer = StringOnlySerializer { output: String::new() };
    let result = "hello".serialize(serializer);
    assert!(result.is_ok());
    
    // But serializing anything else fails
    let serializer = StringOnlySerializer { output: String::new() };
    let result = 42.serialize(serializer);  // Calls serialize_i32
    assert!(result.is_err());
}

Override only the methods you support; Impossible provides error implementations for the rest.

Structure of Impossible

use serde::ser::{self, Impossible};
 
// Impossible definition (simplified):
// pub struct Impossible<Ok, Error> { ... }
 
// It implements all Serializer traits:
// - Serializer
// - SerializeSeq
// - SerializeTuple
// - SerializeTupleStruct
// - SerializeTupleVariant
// - SerializeMap
// - SerializeStruct
// - SerializeStructVariant
 
// Every method returns an error:
fn show_impossible_behavior() {
    let mut impossible: Impossible<(), serde_json::Error> = 
        Impossible::new("unsupported serialization type");
    
    // All methods return errors
    assert!(impossible.serialize_bool(true).is_err());
    assert!(impossible.serialize_i32(42).is_err());
    assert!(impossible.serialize_str("hello").is_err());
    
    // Impossible can be used for sequence types too
    let seq: Impossible<(), serde_json::Error> = Impossible::new("no sequences");
    assert!(seq.serialize_element(&42).is_err());
    assert!(seq.end().is_err());
}

Impossible implements all serializer-related traits with error-returning methods.

Custom Error Messages

use serde::ser::Impossible;
 
fn custom_errors() {
    // Create Impossible with custom error message
    let impossible = Impossible::<(), serde_json::Error>::new(
        "this serializer only supports strings"
    );
    
    // Error message appears in the error
    let result = impossible.serialize_i32(42);
    assert!(result.is_err());
    // Error contains: "this serializer only supports strings"
}

Provide meaningful error messages for unsupported operations.

Partial Serializer Example: JSON Values Only

use serde::{Serializer, Serialize};
use serde::ser::{Impossible, SerializeStruct};
use serde_json::{Value, Error};
 
// Serializer that only produces serde_json::Value
struct ValueSerializer;
 
impl Serializer for ValueSerializer {
    type Ok = Value;
    type Error = Error;
    
    type SerializeSeq = ValueSeqSerializer;
    type SerializeTuple = Impossible<Value, Error>;
    type SerializeTupleStruct = Impossible<Value, Error>;
    type SerializeTupleVariant = Impossible<Value, Error>;
    type SerializeMap = ValueMapSerializer;
    type SerializeStruct = ValueStructSerializer;
    type SerializeStructVariant = ValueStructVariantSerializer;
    
    fn serialize_bool(self, v: bool) -> Result<Value, Error> {
        Ok(Value::Bool(v))
    }
    
    fn serialize_i64(self, v: i64) -> Result<Value, Error> {
        Ok(Value::Number(v.into()))
    }
    
    fn serialize_str(self, v: &str) -> Result<Value, Error> {
        Ok(Value::String(v.to_string()))
    }
    
    fn serialize_none(self) -> Result<Value, Error> {
        Ok(Value::Null)
    }
    
    fn serialize_some<T: ?Sized + Serialize>(self, value: &T) -> Result<Value, Error> {
        value.serialize(self)
    }
    
    // Other primitive types delegate to above implementations
    fn serialize_i8(self, v: i8) -> Result<Value, Error> { self.serialize_i64(v as i64) }
    fn serialize_i16(self, v: i16) -> Result<Value, Error> { self.serialize_i64(v as i64) }
    fn serialize_i32(self, v: i32) -> Result<Value, Error> { self.serialize_i64(v as i64) }
    
    // ... and so on for other types
}
 
// Custom sequence serializer (not Impossible)
struct ValueSeqSerializer(Vec<Value>);
 
impl serde::ser::SerializeSeq for ValueSeqSerializer {
    type Ok = Value;
    type Error = Error;
    
    fn serialize_element<T: ?Sized + Serialize>(&mut self, value: &T) -> Result<(), Error> {
        let serializer = ValueSerializer;
        self.0.push(value.serialize(serializer)?);
        Ok(())
    }
    
    fn end(self) -> Result<Value, Error> {
        Ok(Value::Array(self.0))
    }
}
 
// Map and struct serializers would be similar...

Use Impossible for types you don't support, implement custom serializers for types you do.

Partial Serializer: Struct Fields Only

use serde::{Serializer, Serialize};
use serde::ser::{Impossible, SerializeStruct};
use std::collections::HashMap;
 
// Serializer that only handles structs, collecting field names
struct FieldCollector {
    fields: Vec<String>,
}
 
impl Serializer for FieldCollector {
    type Ok = Vec<String>;
    type Error = serde_json::Error;
    
    // Use Impossible for everything except struct serialization
    type SerializeSeq = Impossible<Vec<String>, serde_json::Error>;
    type SerializeTuple = Impossible<Vec<String>, serde_json::Error>;
    type SerializeTupleStruct = Impossible<Vec<String>, serde_json::Error>;
    type SerializeTupleVariant = Impossible<Vec<String>, serde_json::Error>;
    type SerializeMap = Impossible<Vec<String>, serde_json::Error>;
    type SerializeStruct = StructSerializer;
    type SerializeStructVariant = Impossible<Vec<String>, serde_json::Error>;
    
    // Primitives: return empty result
    fn serialize_bool(self, _: bool) -> Result<Self::Ok, Self::Error> {
        Ok(self.fields)
    }
    
    fn serialize_str(self, _: &str) -> Result<Self::Ok, Self::Error> {
        Ok(self.fields)
    }
    
    // All other primitives similarly...
    
    fn serialize_struct(
        self,
        _name: &'static str,
        _len: usize,
    ) -> Result<Self::SerializeStruct, Self::Error> {
        Ok(StructSerializer { fields: self.fields })
    }
}
 
struct StructSerializer {
    fields: Vec<String>,
}
 
impl SerializeStruct for StructSerializer {
    type Ok = Vec<String>;
    type Error = serde_json::Error;
    
    fn serialize_field<T: ?Sized + Serialize>(
        &mut self,
        key: &'static str,
        _value: &T,
    ) -> Result<(), Self::Error> {
        self.fields.push(key.to_string());
        Ok(())
    }
    
    fn end(self) -> Result<Self::Ok, Self::Error> {
        Ok(self.fields)
    }
}

Only implement the methods you need; Impossible handles the rest.

Combining with Other Serde Patterns

use serde::ser::{self, Impossible, SerializeSeq};
 
// A serializer that counts elements without storing them
struct CountingSerializer;
 
impl ser::Serializer for CountingSerializer {
    type Ok = usize;
    type Error = serde_json::Error;
    
    type SerializeSeq = CountingSeq;
    type SerializeTuple = CountingSeq;
    type SerializeTupleStruct = Impossible<usize, serde_json::Error>;
    type SerializeTupleVariant = Impossible<usize, serde_json::Error>;
    type SerializeMap = Impossible<usize, serde_json::Error>;
    type SerializeStruct = Impossible<usize, serde_json::Error>;
    type SerializeStructVariant = Impossible<usize, serde_json::Error>;
    
    // Primitives return 1 (single element)
    fn serialize_bool(self, _: bool) -> Result<usize, Self::Error> { Ok(1) }
    fn serialize_i8(self, _: i8) -> Result<usize, Self::Error> { Ok(1) }
    fn serialize_str(self, _: &str) -> Result<usize, Self::Error> { Ok(1) }
    // ... etc
    
    fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
        Ok(CountingSeq { count: 0 })
    }
}
 
struct CountingSeq {
    count: usize,
}
 
impl SerializeSeq for CountingSeq {
    type Ok = usize;
    type Error = serde_json::Error;
    
    fn serialize_element<T: ?Sized + ser::Serialize>(&mut self, _: &T) -> Result<(), Self::Error> {
        self.count += 1;
        Ok(())
    }
    
    fn end(self) -> Result<Self::Ok, Self::Error> {
        Ok(self.count)
    }
}

Impossible fills in for types you don't serialize.

Error Type Flexibility

use serde::ser::Impossible;
 
// Impossible works with any error type implementing std::error::Error
use std::fmt;
 
#[derive(Debug)]
struct MyError(String);
 
impl fmt::Display for MyError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}
 
impl std::error::Error for MyError {}
 
fn custom_error_type() {
    // Use Impossible with custom error type
    let impossible = Impossible::<(), MyError>::new(
        MyError("unsupported operation".to_string())
    );
    
    // Or use a closure-based error creation
    let make_impossible = || -> Impossible<(), MyError> {
        Impossible::new(MyError("not implemented".to_string()))
    };
}

Impossible works with any compatible error type.

Complete Example: Query String Serializer

use serde::{Serializer, Serialize};
use serde::ser::{Impossible, SerializeStruct};
use std::fmt;
 
#[derive(Debug)]
struct QueryStringError(String);
 
impl fmt::Display for QueryStringError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}
 
impl std::error::Error for QueryStringError {}
 
// Serializer that produces URL query strings from structs
struct QueryStringSerializer {
    pairs: Vec<(String, String)>,
}
 
impl Serializer for QueryStringSerializer {
    type Ok = String;
    type Error = QueryStringError;
    
    type SerializeSeq = Impossible<String, QueryStringError>;
    type SerializeTuple = Impossible<String, QueryStringError>;
    type SerializeTupleStruct = Impossible<String, QueryStringError>;
    type SerializeTupleVariant = Impossible<String, QueryStringError>;
    type SerializeMap = Impossible<String, QueryStringError>;
    type SerializeStruct = QueryStringStructSerializer;
    type SerializeStructVariant = Impossible<String, QueryStringError>;
    
    fn serialize_struct(
        self,
        _name: &'static str,
        _len: usize,
    ) -> Result<Self::SerializeStruct, Self::Error> {
        Ok(QueryStringStructSerializer { pairs: self.pairs })
    }
    
    // Primitive types convert to strings
    fn serialize_str(self, v: &str) -> Result<String, QueryStringError> {
        Ok(urlencoding::encode(v).to_string())
    }
    
    fn serialize_i64(self, v: i64) -> Result<String, QueryStringError> {
        Ok(v.to_string())
    }
    
    fn serialize_bool(self, v: bool) -> Result<String, QueryStringError> {
        Ok(v.to_string())
    }
    
    // Other primitives not supported in query strings
    fn serialize_none(self) -> Result<String, QueryStringError> {
        Ok(String::new())
    }
}
 
struct QueryStringStructSerializer {
    pairs: Vec<(String, String)>,
}
 
impl SerializeStruct for QueryStringStructSerializer {
    type Ok = String;
    type Error = QueryStringError;
    
    fn serialize_field<T: ?Sized + Serialize>(
        &mut self,
        key: &'static str,
        value: &T,
    ) -> Result<(), QueryStringError> {
        let serializer = QueryStringSerializer { pairs: Vec::new() };
        let value_str = value.serialize(serializer)?;
        self.pairs.push((key.to_string(), value_str));
        Ok(())
    }
    
    fn end(self) -> Result<String, QueryStringError> {
        let query: String = self.pairs
            .iter()
            .map(|(k, v)| format!("{}={}", k, v))
            .collect::<Vec<_>>()
            .join("&");
        Ok(query)
    }
}
 
// Usage
fn to_query_string<T: Serialize>(value: &T) -> Result<String, QueryStringError> {
    value.serialize(QueryStringSerializer { pairs: Vec::new() })
}
 
// urlencoding crate would be needed for actual implementation
mod urlencoding {
    pub fn encode(s: &str) -> String {
        // Simplified - actual implementation would URL-encode
        s.to_string()
    }
}

Build focused serializers with Impossible handling unsupported types.

Type State Pattern with Impossible

use serde::ser::{self, Impossible};
 
// Track what types are supported at compile time
mod supported {
    pub trait StringSupport {}
    pub trait NumberSupport {}
    pub trait SeqSupport {}
}
 
// A serializer that can be configured for what it supports
struct ConfigurableSerializer<StringSupport, NumberSupport, SeqSupport> {
    _marker: std::marker::PhantomData<(StringSupport, NumberSupport, SeqSupport)>,
}
 
// Type aliases for common configurations
type StringOnlySerializer = ConfigurableSerializer<
    (),  // String supported
    Impossible<(), serde_json::Error>,  // Numbers unsupported
    Impossible<(), serde_json::Error>,  // Seq unsupported
>;
 
// This is more of a conceptual pattern
// In practice, you'd implement different serializers for different needs

Use type parameters to express support capabilities.

Debugging Partial Serializers

use serde::ser::Impossible;
use serde::Serialize;
 
// Helper to debug what types a value tries to serialize
fn debug_serialize_types<T: Serialize>(value: &T) -> Vec<&'static str> {
    struct TypeCollector(Vec<&'static str>);
    
    impl serde::Serializer for TypeCollector {
        type Ok = ();
        type Error = serde_json::Error;
        type SerializeSeq = Impossible<(), serde_json::Error>;
        type SerializeTuple = Impossible<(), serde_json::Error>;
        type SerializeTupleStruct = Impossible<(), serde_json::Error>;
        type SerializeTupleVariant = Impossible<(), serde_json::Error>;
        type SerializeMap = Impossible<(), serde_json::Error>;
        type SerializeStruct = Impossible<(), serde_json::Error>;
        type SerializeStructVariant = Impossible<(), serde_json::Error>;
        
        fn serialize_bool(mut self, _: bool) -> Result<Self::Ok, Self::Error> {
            self.0.push("bool");
            Ok(())
        }
        
        fn serialize_i32(mut self, _: i32) -> Result<Self::Ok, Self::Error> {
            self.0.push("i32");
            Ok(())
        }
        
        fn serialize_str(mut self, _: &str) -> Result<Self::Ok, Self::Error> {
            self.0.push("str");
            Ok(())
        }
        
        // ... other primitive types
    }
    
    let collector = TypeCollector(Vec::new());
    let _ = value.serialize(collector);
    // Return collected type names
    vec
![]  // Simplified
}

Use Impossible as a foundation for debugging serializers.

Synthesis

Quick reference:

Aspect Description
Purpose Base implementation for partial serializers
Methods All return Err with custom message
Traits Implements Serializer, SerializeSeq, SerializeMap, etc.
Generics Impossible<Ok, Error>
Usage Inherit for unsupported types, override for supported

Common pattern:

use serde::Serializer;
use serde::ser::Impossible;
 
struct MySerializer;
 
impl Serializer for MySerializer {
    type Ok = MyOkType;
    type Error = MyError;
    
    // Use Impossible for types you don't support
    type SerializeSeq = Impossible<MyOkType, MyError>;
    type SerializeTuple = Impossible<MyOkType, MyError>;
    type SerializeTupleStruct = Impossible<MyOkType, MyError>;
    type SerializeTupleVariant = Impossible<MyOkType, MyError>;
    type SerializeMap = Impossible<MyOkType, MyError>;
    type SerializeStruct = MyStructSerializer;  // Support this one
    type SerializeStructVariant = Impossible<MyOkType, MyError>;
    
    // Implement only the methods you support
    fn serialize_str(self, v: &str) -> Result<MyOkType, MyError> {
        // Your implementation
        todo!()
    }
    
    // All other methods inherited from Impossible return errors
}

Key insight: serde::ser::Impossible is the "null serializer" that provides error implementations for all Serializer trait methods, saving you from writing boilerplate "unsupported" stubs when implementing a partial serializer. Without Impossible, you'd need to implement every serialize_* method to return an error manually—over 20 methods for the Serializer trait alone, plus methods for SerializeSeq, SerializeMap, SerializeStruct, and all variant serializers. Impossible lets you express "I don't support this type" through type aliases while focusing your implementation on the types you do support. It's particularly useful when building serializers for specific domains (like query strings, form data, or configuration formats) that don't need to handle arbitrary Rust data types. The error message passed to Impossible::new helps developers understand what's supported when they accidentally try to serialize an unsupported type.