Returning a generic error complains about concrete types

I want to create a default implementation for a trait member function that deserializes into some type, returns the error making sure it is mappable to some other error type provided by the implementing party as an associated type.

Originally I tried making the transform function generic over D returning Result<Self::Payload, D::Error > with D bound to the Deserializer trait but this is problematic for type inference as a default implementation. So I reached for E as a generic over the error type instead.

I expected this to work but when compiling I get the following error:

error[E0308]: mismatched types
  --> src/main.rs:43:9
   |
39 |     fn transform<E>(&self, buf: &[u8]) -> Result<Self::Payload, E>
   |                  - this type parameter    ------------------------ expected `Result<SomePayload, E>` because of return type
...
43 |         serde_json::from_slice(buf)
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected type parameter `E`, found struct `serde_json::Error`
   |
   = note: expected enum `Result<SomePayload, E>`
              found enum `Result<_, serde_json::Error>`

Code:

use serde::Deserialize;
use serde_json::Error;
 
pub trait SomeTrait<'de> {
    type Payload: Deserialize<'de>;
    type Error;
 
    fn accept(&self, msg: Result<Self::Payload, Self::Error>);
    fn transform<E>(&self, buf: &[u8]) -> Result<Self::Payload, E>
    where
        Self::Error: From<E>;
 
    fn default_impl(&self) {
        let transformed = self.transform(&[]);
        let transformed = transformed.map_err(Into::into);
        self.accept(transformed);
    }
}
 
#[derive(Deserialize)]
struct SomePayload;
 
enum SomeError {}
 
impl From<serde_json::Error> for SomeError {
    fn from(_: Error) -> Self {
        todo!()
    }
}
 
struct SomeStruct;
 
impl<'de> SomeTrait<'de> for SomeStruct {
    type Payload = SomePayload;
    type Error = SomeError;
 
    fn accept(&self, msg: Result<Self::Payload, Self::Error>) {}
 
    fn transform<E>(&self, buf: &[u8]) -> Result<Self::Payload, E>
    where
        Self::Error: From<E>,
    {
        serde_json::from_slice(buf)
    }
}
 
fn main() {
    let s = SomeStruct;
 
    s.default_impl();
}

If the transform() function returns Result<Payload, E> where E is a type parameter, then you can't return a Result<Payload, serde_json::Error>. This is because generic parameters are chosen by the caller, so this wouldn't make sense – what should happen if the caller choses E = SomeOtherError, which is not the same as serde_json::Error?

You should just return Result<Self::Payload, serde_json::Error> instead, probably.

2 Likes

Is there a way to make it generic for any type that can be mapped to Self::Error or achieve my ambition of following transform with accept for different types of deserializers?

Could I make the trait generic and write an impl<E> block?

Since you already have a Self::Error: From<E> bound, and an impl From<serde_json::Error> for SomeError, then you could replace the bound with where Self::Error: From<serde_json::Error>.

I understand that I would be able to make it work like that but as I understand it, in doing so, I would be restricting the entire trait to work only with deserializers that produce serde_json::Error types.

By contrast, I would like to write the trait in a way where many deserializers with distinct error types can be used such that it would work with serde_toml::Error, serde_yaml::Error and whatever else you can imagine.

Is there a way to achieve that? :slight_smile:

Is returning Self::Error and setting type Error = <whatever_serializer>::Error (in each concrete implementation) what you are looking for?