Expected closure, found a different closure

前端 未结 2 2010
借酒劲吻你
借酒劲吻你 2020-12-07 02:25

A is a structure which contains a vector of B. A implements the add_b method which adds a B instance to the

相关标签:
2条回答
  • 2020-12-07 02:42

    In this specific example, you can avoid a trait object by using a function pointer instead:

    struct B {
        f: fn(),
    }
    
    impl B {
        fn new(f: fn()) -> B {
            B { f: f }
        }
    }
    

    The rest of Lukas Kalbertodt's answer is unchanged:

    struct A {
        b_vec: Vec<B>,
    }
    
    impl A {
        fn new() -> A {
            A { b_vec: Vec::new() }
        }
    
        fn add_b(&mut self, b: B) {
            self.b_vec.push(b);
        }
    }
    
    fn main() {
        let mut a = A::new();
        a.add_b(B::new(|| println!("test")));
        a.add_b(B::new(|| println!("test2")));
    }
    

    This is only valid because your closures do not capture any environment. The Rust compiler is thus able to "promote" them to full functions and then take references to the implicit function.

    0 讨论(0)
  • 2020-12-07 03:00

    It's always worth taking a look at the full compiler output:

    error[E0308]: mismatched types
      --> src/main.rs:39:20
       |
    39 |     a.add_b(B::new(|| println!("test2")));
       |                    ^^^^^^^^^^^^^^^^^^^^ expected closure, found a different closure
       |
       = note: expected type `[closure@src/main.rs:38:20: 38:39]`
                  found type `[closure@src/main.rs:39:20: 39:40]`
       = note: no two closures, even if identical, have the same type
       = help: consider boxing your closure and/or using it as a trait object
    

    Especially helpful:

    • no two closures, even if identical, have the same type

    • consider boxing your closure and/or using it as a trait object

    We can simplify your example further by removing the type B altogether. Then the only task is to save a vector of closures. As the compiler tells us, no two closures have the same type, but Vec is a homogeneous data structure, meaning that every item in it has the same type.

    We can work around that restriction by introducing one level of indirection. As the compiler suggests, this can either be done by trait objects or boxing (the latter kind of includes the first one). The corresponding types would look like this:

    • Vec<&Fn()> (reference to trait objects)
    • Vec<Box<Fn()>> (trait object in a box)

    In your example you want to own all closures, thus the correct choice is to box all closures, as Box<T> is an owning wrapper while references only borrow stuff.

    A fully working example:

    struct A {
        b_vec: Vec<B>,
    }
    
    impl A {
        fn new() -> A {
            A { b_vec: Vec::new() }
        }
    
        fn add_b(&mut self, b: B) {
            self.b_vec.push(b);
        }
    }
    
    struct B {
        f: Box<Fn()>,
    }
    
    impl B {
        fn new<F>(f: F) -> B
        where
            F: Fn() + 'static,
        {
            B { f: Box::new(f) }
        }
    }
    
    fn main() {
        let mut a = A::new();
        a.add_b(B::new(|| println!("test")));
        a.add_b(B::new(|| println!("test2")));
    }
    
    0 讨论(0)
提交回复
热议问题