1

I am currently trying to implement a trait from a library (in my case serde), which expects a trait to be returned (see example function next), but i only have a supertrait available, but i do not know how to downcast a supertrait to a subtrait.

Example Code:

/// Imagine this type as a trait from a library, which i cannot change
trait SubTrait {
    fn get_bool() -> bool;
}

trait SuperTrait: SubTrait {
    fn get_string() -> String;
}

/// Imagine this type as a trait from a library, which i cannot change
trait GenericTrait {
    fn next<T>(&mut self) -> Result<Option<T>, std::io::Error>
    where
        T: SubTrait;
}

struct SomeOtherStruct<'d, V: SuperTrait> {
    field1:  V,
    _marker: std::marker::PhantomData<&'d ()>,
}

impl<'d, V> SomeOtherStruct<'d, V>
where
    V: SuperTrait,
{
    pub fn new(field: V) -> Self {
        return Self {
            field1:  field,
            _marker: std::marker::PhantomData,
        };
    }

    pub fn do_something(self) -> Result<Option<V>, std::io::Error> {
        return Ok(Some(self.field1));
    }
}

struct ImplementingStruct<'d, V: SuperTrait> {
    field1:  V,
    _marker: std::marker::PhantomData<&'d ()>,
}

/// Trying to implement the librarie's trait, while using a supertrait
impl<'d, V> GenericTrait for ImplementingStruct<'d, V>
where
    // i have also already tried using "SuperTrait + SubTrait", but the same error happens
    V: SuperTrait,
{
    fn next<T>(&mut self) -> Result<Option<T>, std::io::Error>
    where
        T: SubTrait,
    {
        // Error: Expected "Option<T>" found "Option<V>"
        return SomeOtherStruct::new(self.field1).do_something();
        // TL;DR: how to cast "Option<V>" to "Option<T>"
    }
}

rust version: 1.58.1

Notes:

  • i know This Rust Issue exists, but i dont think it is related (it is about SubTrait -> SuperTrait, but this question is about SuperTrait -> SubTrait)
  • from what i can tell, Boxing is not a option here
hasezoey
  • 998
  • 9
  • 24
  • 5
    In your code, `SubTrait` is a supertrait of `SuperTrait`, which is... confusing. – myrtlecat Jan 27 '22 at 11:19
  • 1
    Note to curators: this can probably be marked as a duplicate of *some* question, but the OP's constraint of not being able to change `GenericTrait` makes it not exactly the same as any of the ones I linked in my answer. It is likely I have overlooked one; there are a *lot* of `expected type parameter, found ` questions. – trent Jan 27 '22 at 12:01
  • @myrtlecat from my understanding from reading the docs & other questions (and also coming from another language), i thought a "supertrait" is a trait that requires a "subtrait" to be implemented (like js class `extends`) – hasezoey Jan 27 '22 at 14:21
  • It is the other way around, to quote [the book](https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#using-supertraits-to-require-one-traits-functionality-within-another-trait): "The trait you rely on is a supertrait of the trait you’re implementing." and [Rust by Example](https://doc.rust-lang.org/rust-by-example/trait/supertraits.html) gives an example `trait Student: Person {...}` noting that "Person is a supertrait of Student". – myrtlecat Jan 27 '22 at 16:50
  • It's the same idea as supersets: a set A is a superset of set B if any element of B is also included in A. – Chayim Friedman Jan 28 '22 at 01:07

1 Answers1

3

You cannot implement GenericTrait this way.

trait GenericTrait {
    fn next<T>(&mut self) -> Result<Option<T>, std::io::Error>
    where
        T: SubTrait;
}

This trait means: For any type T the caller asks for, if T: SubTrait, next will return a T (wrapped in Result<Option...>). So, if the caller asks for String, next returns a String; if the caller asks for i32, next returns an i32; etc.

impl<'d, V> GenericTrait for ImplementingStruct<'d, V>
where
    V: SuperTrait,
{
    fn next<T>(&mut self) -> Result<Option<T>, std::io::Error>
    where
        T: SubTrait,
    {
        // Error: Expected "Option<T>" found "Option<V>"
        return SomeOtherStruct::new(self.field1).do_something();
        // TL;DR: how to cast "Option<V>" to "Option<T>"
    }
}

This function body says: For any type T the caller asks for, return V instead. If the caller asks for String, you would return V. If the caller asks for i32, you would still return V.

You have broken your promise to the compiler: you said in the function signature you'd return a T, but you're returning a V instead. This isn't possible and has nothing to do with supertraits or subtraits (which are, as pointed out in the question comments, reversed in your example). You can't write a function whose body and signature are in conflict.

In similar cases, sometimes it is appropriate to change the function signature. However, you can't change the function signature because that would make impl GenericTrait for ImplementingStruct untrue. You can't implement a trait by not implementing the trait.

It is not clear what a solution might be in your case, but it is almost certainly unrelated to downcasting or supertraits. The same error happens with only one trait.

Note also that GenericTrait is not a generic trait (that is, a trait with generic parameters). If it were, that is, if it were defined as trait GenericTrait<T>, then you could simply implement GenericTrait<V> for ImplementingStruct<V>. That the method is generic and not the trait is what makes it impossible to implement in this way.

Similar Questions

trent
  • 25,033
  • 7
  • 51
  • 90
  • thanks for the clarification, i have read through many of the "similar questions" before having posted my question, but thought they are rather unrelated (because seemingly i did not understand it correctly), my current (non example) case is implementing `serde::de::SeqAccess` (and later other versions for `serde::de::Visitor`) where `next` in this case would be `next_element` where inside i would like to call a trait function that the top-level struct (the one to be deserialized) implements, but the problems is because i dont know what `T` is i cannot implement a conversion for `V` to `T` – hasezoey Jan 27 '22 at 14:29
  • That's correct, you cannot implement `SeqAccess` for "only some" `T`s. Note also that `next_element` [should not be overridden at all](https://docs.serde.rs/serde/de/trait.SeqAccess.html#method.next_element); you should implement `SeqAccess` by providing `next_element_seed`. Overriding `next_element` is not likely to do anything useful, even without the type error. But in any case, `SeqAccess` can't be implemented specific to `T`. Type-specific deserialization logic goes in `Deserialize` and `Visitor` implementations. – trent Jan 27 '22 at 15:32
  • "Type-specific deserialization logic goes in Deserialize and Visitor implementations", that is what i am currently doing with `visit_seq` (requires the type to implement `SeqAccess`, which i am doing), but yes i will try to do it in "next_element_seed" – hasezoey Jan 28 '22 at 06:19