Compile error using "impl trait" from edition 2018


#1

Hello, I’m trying to compile this code with edition 2018 (using impl SomeTrait, in the return type of some functions; it’s failing, the error is “mismatched types” but the “expected type” is the same that “found type”;

how it’s possible?
It can be fixed or is a concept error?

Note: this example use the crate indextree

    trait CommonTrait {}

    struct First {}
    impl CommonTrait for First {}

    struct AnotherFirst {}
    impl CommonTrait for AnotherFirst {}

    pub fn get_first() -> impl CommonTrait {
        First {}
    }

    pub fn get_another_first() -> impl CommonTrait {
        AnotherFirst {}
    }

    fn main() {
        println!("Hello, world!");
        use indextree::Arena;

        // This doesn't works
        {
            // Create a new arena
            let arena = &mut Arena::new();

            let first_tag = get_first();
            let a = arena.new_node(&first_tag);

            let second_tag = get_another_first();
            let b = arena.new_node(&second_tag);
        }
       
        // This works
        {
            // Create a new arena
            let arena = &mut Arena::new();

            // let first_tag = get_first();
            let first_tag = get_another_first();
            let a = arena.new_node(&first_tag);

            // let second_tag = get_first();
            let second_tag = get_another_first();
            let b = arena.new_node(&second_tag);
        }
        
    }
   error[E0308]: mismatched types
      --> src\main.rs:30:32
       |
    30 |         let b = arena.new_node(&second_tag);
       |                                ^^^^^^^^^^^ expected opaque type, found a different opaque type
       |
       = note: expected type `&impl CommonTrait` (opaque type)
                  found type `&impl CommonTrait` (opaque type)

    error: aborting due to previous error

    For more information about this error, try `rustc --explain E0308`.

    For more information about this error, try `rustc --explain E0308`.
    error: Could not compile `bug`.
    */

#2

The compiler flat-out tells you what the problem is:

expected opaque type, found a different opaque type

The two functions are returning different types. Different types aren’t compatible. impl Trait does not change this.


#3

You may be able to get around this by using trait objects.


#4

impl Trait in return position is not a real type abstraction. It’s more like autocomplete for your type name, so you don’t have to type it yourself.

Your impl trait functions are still roughly equivalent to:

    pub fn get_first() -> First {
        First {}
    }

    pub fn get_another_first() -> AnotherFirst {
        AnotherFirst {}
    }

The impl Trait in return position hides type name, but doesn’t change the code in any way.

If you want dynamic behavior, you should use dyn Trait.


#5

Thanks to all

Reading Trait Objects and using the dyn syntax, I got this code (works)

pub trait CommonTrait: std::fmt::Debug {}

#[derive(Debug)]
pub struct First {}
impl CommonTrait for First {}

#[derive(Debug)]
pub struct AnotherFirst {}
impl CommonTrait for AnotherFirst {}

pub fn get_first() -> Box<dyn CommonTrait> {
    Box::new(First {})
}

pub fn get_another_first() -> Box<dyn CommonTrait> {
    Box::new(AnotherFirst {})
}

fn main() {
    println!("Hello, world!");
    use indextree::Arena;

    // This now works :)
    {
        // Create a new arena
        let arena = &mut Arena::new();

        let first_tag = get_first();
        let a = arena.new_node(&first_tag);

        let second_tag = get_another_first();
        let b = arena.new_node(&second_tag);

        println!("{:?}", arena)
    }
   
    // This too works
    {
        // Create a new arena
        let arena = &mut Arena::new();

        // let first_tag = get_first();
        let first_tag = get_another_first();
        let a = arena.new_node(&first_tag);

        // let second_tag = get_first();
        let second_tag = get_another_first();
        let b = arena.new_node(&second_tag);

        println!("{:?}", arena)
    }
    
}