How to implement a trait only for types that do not borrow?

Hello! I created this minimal example for a problem I'm facing. I have the following trait that is easily implemented for owned types:

trait Deserialize: Sized {
    type Error;

    fn deserialize(buf: &mut &[u8]) -> Result<Self, Self::Error>;
}

struct Owned([u8; 2]);

impl Deserialize for Owned {
    type Error = TryFromSliceError;

    fn deserialize(buf: &mut &[u8]) -> Result<Self, Self::Error> {
        let (data, rest) = buf.split_at(2);
        let owned = Owned(data.try_into()?);
        *buf = rest;
        Ok(owned)
    }
}

I also have this extension trait implemented for all implementers of Deserialize:

trait FromHex: Deserialize {
    fn from_hex(hex: impl AsRef<[u8]>) -> Result<Self, Self::Error> {
        let bytes = hex::decode(hex).unwrap(); // error ignored in the example
        Self::deserialize(&mut &bytes[..])
    }
}

impl<T> FromHex for T where T: Deserialize {}

This compiles. However, if I want to implement Deserialize for borrowed types, I have to add lifetimes to it:

struct Borrowed<'a>(&'a [u8]);

impl<'a> Deserialize<'a> for Borrowed<'a> {
    type Error = ();

    fn deserialize(buf: &mut &'a [u8]) -> Result<Self, Self::Error> {
        Ok(Borrowed(buf))
    }
}

And of course this breaks FromHex, since the bytes slice in its implementation does not live long enough.

My question is: how can I allow Deserialize to be implemented for both owned and borrowed types, and implement FromHex only for types that are both owned and Deserialize?

In other words, I would need something like trait FromHex: Deserialize<'_>, which is not valid syntax. What's the most idiomatic solution here? Create separate Deserialize traits for owned and borrowed types? Change the buf parameter type to something else? Thank you in advance!

Edit: I tried using HRTB but couldn't find a solution either. Here's a playground.

Judging by the similarity with Serde, you probably want a HRTB:

trait FromHex: for<'a> Deserialize<'a> { ... }
1 Like

Sorry, I should've mentioned that I tried that, but then I get compile errors caused by the Self::Error associated type. I couldn't find a way around it, here's a playground.

You can force the error to have to be the same across all lifetimes like so:

trait FromHex: for<'a> Deserialize<'a, Error = Self::CommonError> {
    type CommonError;
    fn from_hex(hex: impl AsRef<[u8]>) -> Result<Self, Self::CommonError> {
        let bytes = hex::decode(hex).unwrap();
        Self::deserialize(&mut &bytes[..])
    }
}

And there are other alternatives like making sure you can convert to a common error type.

1 Like

That's solvable with a redundant Error associated type.

2 Likes

Thank you very much @quinedot and @paramagnetic!

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.