Don't recognize the implemented trait?

use std::convert::From;
use std::error::Error;
use std::fmt::Display;
use std::fmt::Formatter;
use std::fmt::Result;

#[derive(Debug)]   
pub struct ErrorContext<E> where E: Error
{
    previous: Option<E>,
    message: String
}
impl<'this, E> ErrorContext<E> where E: Error
{
    pub fn new(previous: Option<E>, message: String) -> Self {
        return Self {
            previous,
            message
        }
    }

    pub fn get_previous(&'this self) -> &'this Option<E> {
        return &self.previous;
    }

    pub fn get_message(&'this self) -> &'this String {
        return &self.message;
    }
}




#[derive(Debug)]   
pub enum First<E>
where E: Error
{
    F(ErrorContext<E>)
}
impl<E> Display for First<E> 
where E: Error
{
    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
        return Ok(());
    }
}
impl<E> Error for First<E> where E: Error {}




#[derive(Debug)]
pub enum Second<E>
where E: Error
{
    F(ErrorContext<E>),
    S(ErrorContext<E>)
}
impl<E> Display for Second<E>
where E: Error
{
    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
        return Ok(());
    }
}
impl<E> Error for Second<E>
where E: Error
{}
impl<E> From<First<E>> for Second<E> where E: Error
{
    fn from(previous: First<E>) -> Self {
        return Second::F(
            ErrorContext::new(Some(previous), "Second message".to_string())
        );
    }
}

And:

error[E0308]: mismatched types
   --> bin/core/./src/postgres_fixtures.rs:100:36
    |
96  | impl<E> From<First<E>> for Second<E> where E: Error
    |      - this type parameter
...
100 |             ErrorContext::new(Some(previous), "Second message".to_string())
    |                                    ^^^^^^^^ expected type parameter `E`, found enum `First`
    |
    = note: expected type parameter `E`
                         found enum `First<E>`

Why does not compile?

A Second<E> can store an ErrorContext<E>, however in your From implementation you're trying to give it a ErrorContext<First<E>>.

To solve this you can either store an ErrorContext<First<E>> in Second::F or get the inner ErrorContext<E> from previous before calling ErrorContext::new

" A Second<E> can store an ErrorContext<E> , however in your From implementation you're trying to give it a ErrorContext<First<E>> ." - you said.

But we have impl<E> Error for First<E> where E: Error {}, and E: Error, so why it does not work?

You might be be confusing this with OOP and garbage collected languages like Java where you can assign an instance of a type to a variable that expect an interface it implements. That's possible because everything is a pointer and interfaces are implemented with dynamic dispatch.

In rust however static dispatch is the default one. ErrorContext<E> and ErrorContext<First<E>> are different types and you can't store one where the other is expected. The fact that E and First<E> both implement the same trait doesn't matter.

What about it?

impl<E, W> From<First<W>> for Second<E> where E: Error, W: Error
{
    fn from(previous: First<W>) -> Self {
        return Second::<E>::F(
            ErrorContext::<E>::new(Some(previous), "Second message".to_string())
        );
    }
}

In the Generic parameter field, we can put any type that meets these Generic constraints.
So, First<W> can be as E
But it does not compile (

No, First<W> is a type and E is another type.

You may impl From<First<E>> for Second<First<E>> but that doesn't seems practical to me. I requote my previous suggestions:

I can write same code that will compile. Thanks for your advice.
But i want to know what problem in that code.
E - is generic type, First<W> is concrete type for E.
The code below:

impl<E, W> From<First<W>> for Second<E> where E: Error, W: Error
{
    fn from(previous: First<W>) -> Self {
        return Second::<E>::F(
            ErrorContext::<E>::new(Some(previous), "Second message".to_string())
        );
    }
}

has Some(E: Error) as Some(previous),where previous is First<W> and we have impl<E> Error for First<E> where E: Error {} at same time. So, the rule about working with generics is performed.

First<W> is not the concrete type for E, they're two completly different types. The concrete type for E will be chosen in each use of your impl.

We include different meanings in words "concrete type for generic."
A concrete implementation and subsequent compilation implies the definition of generic-parameters. In this code in place of E there will always be First <W>, where enum First will never change, so here we can consider it a concrete type.
"expected type parameter E, found enum First" - compilator said;
So, For all W: Error in First<W> we have Error trait implementation.
Generic E accept any type that has Error trait implementation.
It must be compiled

That's not how it works. The concrete type for E is choosen by the user of your implementation.

It's not that it can accept any type that implement the Error trait, it must accept any type that implement the Error type. So what happens if a downstream user use your implementation where E is not First<W>? i.e. what should <Second<Foo> as From<First<Bar>>>::from(previous) do, assuming Foo and Bar both implement Error? The implementation would try to store First<Bar> where a Foo would be expected, and that's not possible.

1 Like

Yes. You are absolutely right. You just opened my eyes. But I expected a counter-example much earlier.

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.