Extending third party traits

Is there a way to extend third party traits in current stable Rust (without specialization)?

Background is that I was looking into extending Serde with things that won't be accepted in the core.

I've created a minimal example showing the problem (Playground link):

// This is about issued with extending third party traits.

// Imagine the next two traits are from a third party in a separate crate

trait ThirdPartyData {
    fn data(&self) -> u8;
}

trait ThirdParty {
    fn hello<D>(&self, data: D)
    where
        D: ThirdPartyData;
}

// My implementation for the `ThirdPartyData` trait

struct MyData {
    value: u8,
}

impl ThirdPartyData for MyData {
    fn data(&self) -> u8 {
        self.value
    }
}

// I define an extentsion trait to have additional functionality on the `data` item

trait ThirdPartyDataExt: ThirdPartyData {
    fn more_info(&self) -> u8;
}

// This is an implementation of a third party trait where I would like to call a method from my
// extension trait

struct MyStruct {}

impl ThirdParty for MyStruct {
    fn hello<D>(&self, data: D)
    where
        D: ThirdPartyDataExt,
        // I've tried adding the extension trait as additional bound, but that doesn't work as
        // it's an extra requirement the original trait doesn't have
        //D: ThirdPartyData + ThirdParyDataExt,
    {
        println!("data: {:?}", data.data());
        // The next line is the problem, as `data` doesn't have the correct trait bounds
        println!("more_info: {:?}", data.more_info());
    }
}

fn main() {
    let my_data = MyData { value: 6 };
    let my_struct = MyStruct {};
    my_struct.hello(my_data);
}

You could add a blanket implementation for ThirdPartyData, but then you couldn't implement ThirdPartyDataExt for MyData:

impl<T: ThirdPartyData> ThirdPartyDataExt for T {
    fn more_info(&self) -> u8 {
        1
    }
}

I hope that I'm missing something and there's a solution (that might even be completely different to the way I tried it).

I don't think this is possible – traits can, as far as I know, never be implemented with bounds stricter than their declaration.

In this case, knowing what you want to achieve is useful: I would suggest you create a subtrait of ThirdParty (would it be Deserialize?), e.g. ThirdPartyExt, which in turn has fn hello<D: ThirdPartyDataExt> then you could start using this trait bound instead of the original.

If this trait is indeed Deserialize, you can even write a corresponding #[derive] macro for it rather easily, I think. Of course then you'd need to add #[derive(Deserialize, DeserializeExt)] to whatever data structures you are dealing with.

The case is about tagged values support in Serde. I tried to implement this commit with extending Serde.

More specifically line 31 in value/ser.rs:

impl serde::Serialize for Value {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        match *self {
…
            Value::Tag(ref t, ref v) => serializer.serialize_tagged_value("cbor", t, v),
…
        }
    }
}

The problem is that serialize_tagged_value() is only implemented on the extension trait (it corresponds to the more_info() call in the minimal example above). I also can't change the trait bounds as serialize() is defined by the serde::Serialize trait.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.