rustserde

How to implement Debug for serde::ser::Error


I am writing a custom Serializer for serde. For it, I also need a custom error type. Serde requires me to implement serde::ser::Error for this. serde::ser::Error in turn requires me to implement a custom() method where the parameter is only limited by the std::fmt::Display trait bound. So I came up with this:

use std::fmt::{Debug, Display, Formatter};

#[derive(Debug)]
pub enum Error {
    Custom(Box<dyn Display>),
    CannotSerializeMap,
}

impl Display for Error {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::Custom(error) => Display::fmt(error, f),
            Self::CannotSerializeMap => write!(f, "cannot serialize map"),
        }
    }
}

impl std::error::Error for Error {}

impl serde::ser::Error for Error {
    fn custom<T>(msg: T) -> Self
    where
        T: Display,
    {
        Self::Custom(Box::new(msg))
    }
}

However, I cannot derive Debug, which is necessary for std::error::Error which in turn is necessary for serde::ser::Error, since Box<dyn Display> does not implement Debug. I cannot implement Debug for it, since neither Box nor the trait Debug itself are in my crate. I also cannot add an appropriate constraint for T in custom() since it would break the trait's contract.

How can I solve this?


Solution

  • You can use a little trick, based on two facts:

    Therefore, whenever you have some T: Display, you can instead of Box<dyn Display> get a Box<dyn Error>, like this (playground):

    use std::fmt::{Debug, Display, Formatter};
    
    #[derive(Debug)]
    pub enum Error {
        Custom(Box<dyn std::error::Error>),
        CannotSerializeMap,
    }
    
    impl serde::ser::Error for Error {
        fn custom<T>(msg: T) -> Self
        where
            T: Display,
        {
            Self::Custom(msg.to_string().into())
        }
    }
    

    And then #[derive(Debug)] works out of the box (pun unintended), since dyn Error is Debug.