Expected opaque type, found a different opaque type with `Fn`

Hello, basically here is a link to othe playground : Rust Playground

#![allow(unused)]
use std::collections::HashMap;

#[derive(PartialEq, Debug)]
pub struct Tag {
    pub name: String,
    pub attributes: HashMap<String, String>
}

pub fn tag_name_predicate(name: String) -> impl Fn(&Tag) -> bool {
    move |tag: &Tag| tag.name == name
}

pub fn id_predicate(id: String) -> impl Fn(&Tag) -> bool {
    move |tag: &Tag| {
        if let Some(actual_id) = tag.attributes.get(&"id".to_string()) {
            return *actual_id == id;
        }
        false
    }
}

pub fn and_predicate(predicates: Vec<impl Fn(&Tag) -> bool>) -> impl Fn(&Tag) -> bool {
    move |tag: &Tag| {
        predicates
            .iter()
            .fold(true, |acc, predicate| acc && predicate(&tag))
    }
}

fn main() {
    let mut map = HashMap::new();
    map.insert("id".to_string(), "foo".to_string());
    let tag = Tag {
        name: String::from("div"),
        attributes: map,
    };

    let tag_name_matcher = tag_name_predicate(String::from("div"));
    let id_matcher = id_predicate(String::from("foo"));

    let matcher = and_predicate(vec![tag_name_matcher, id_matcher]);

    let does_match = matcher(&tag);

    assert!(does_match);
}

combining 2 predicates is rejected at compile time with the error

error[E0308]: mismatched types
  --> src/main.rs:42:56
   |
14 | pub fn id_predicate(id: String) -> impl Fn(&Tag) -> bool {
   |                                    --------------------- the found opaque type
...
42 |     let matcher = and_predicate(vec![tag_name_matcher, id_matcher]);
   |                                                        ^^^^^^^^^^ expected opaque type, found a different opaque type
   |
   = note:     expected type `impl for<'r> Fn<(&'r Tag,)>` (opaque type at <src/main.rs:10:44>)
           found opaque type `impl for<'r> Fn<(&'r Tag,)>` (opaque type at <src/main.rs:14:36>)
   = note: distinct uses of `impl Trait` result in different opaque types

Please advise

In other words, you cannot mix and match different impl Trait returns, even if they have the same trait bound. They could be different concrete types underneath; even if they are the same today, the author is maintaining the flexibility to change them tomorrow. (Closures also always have different types, though.)

One option is to return Box<dyn Fn(&Tag) -> bool> instead. dyn Fn(&Tag) -> bool is a concrete type and Box supplies the required indirection due to dyn Trait being unsized.

1 Like

Thank you ! I am learning and understanding Rust more ! :slightly_smiling_face::+1:

1 Like