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 needsUse 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.
