【问题标题】:Expected closure, found a different closure预期关闭,发现不同的关闭
【发布时间】:2016-08-22 15:26:16
【问题描述】:

A 是一个包含B 向量的结构。 A 实现了add_b 方法,该方法将B 实例添加到B 的列表中。 B 包含一个闭包属性 f

如果我在带有add_b 的向量中添加一个B,就可以了。如果我用add_b 添加两个向量,我得到一个错误,说两个闭包是不同的。这是一个最小的例子:

// A struct...
struct A<F> {
    b_vec: Vec<B<F>>, // A vector of B
}

// ...and its implementation
impl<F> A<F>
where
    F: Fn(),
{
    fn new() -> A<F> {
        A { b_vec: Vec::new() }
    }

    fn add_b(&mut self, b: B<F>) {
        self.b_vec.push(b);
    }
}

// B struct...
struct B<F> {
    f: F,
}

// ...and its implementation
impl<F> B<F>
where
    F: Fn(),
{
    fn new(f: F) -> B<F> {
        B { f: f }
    }
}

// I add two B (with their closures arguments) in A
fn main() {
    let mut a = A::new();
    a.add_b(B::new(|| println!("test")));
    a.add_b(B::new(|| println!("test2")));
}

这段代码导致:

error[E0308]: mismatched types
  --> src/main.rs:39:20
   |
39 |     a.add_b(B::new(|| println!("test2")));
   |                    ^^^^^^^^^^^^^^^^^^^^ expected closure, found a different closure
   |

如何将多个具有不同闭包的B 添加到Ab_vec

【问题讨论】:

    标签: rust


    【解决方案1】:

    看看完整的编译器输出总是值得一看的:

    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
    

    特别有帮助:

    • 没有两个闭包,即使相同,也有相同的类型

    • 考虑将闭包装箱和/或将其用作特征对象

    我们可以通过完全删除 B 类型来进一步简化您的示例。那么唯一的任务就是保存一个闭包向量。正如编译器告诉我们的那样,没有两个闭包具有相同的类型,但Vec 是同构数据结构,这意味着其中的每个项目都具有相同的类型。

    我们可以通过引入一层间接来解决这个限制。正如编译器所建议的那样,这可以通过特征对象或装箱来完成(后一种包括第一种)。对应的类型如下所示:

    • Vec&lt;&amp;Fn()&gt;(对特征对象的引用)
    • Vec&lt;Box&lt;Fn()&gt;&gt;(盒子里的特征对象)

    在您的示例中,您希望拥有所有闭包,因此正确的选择是装箱所有闭包,因为Box&lt;T&gt; 是拥有的包装器,而引用仅借用东西。

    一个完整的例子:

    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")));
    }
    

    【讨论】:

    • 谢谢!仅供参考:我想我理解了你写的内容,现在修复我的代码并不容易(我还是个初学者)。我通过删除B(好的,检查)来简化我的代码,然后尝试在结构声明中添加Vec&lt;Box&lt;F&gt;&gt;,然后在add_b 中添加Box::new(...),但仍然有相同的错误。不确定我错过了什么,我仍然需要挖掘。
    • 如果闭包实际上没有从环境中捕获任何变量,那么fn() 是一个合适的类型并且不需要装箱。
    • @rap-2-h 你必须写Vec&lt;Box&lt;Fn()&gt;&gt; 而不是F。这很重要。这样我们确实删除了所有类型参数。这是一个有效的版本:play.integer32.com/?gist=b387fb2439171547470e0047ec0f8f14(这在add_b 之外进行装箱;您可以通过向函数添加类型参数而不是A 在内部进行)
    • @Shepmaster 我也找到了那个,但不认为它是重复的。链接问题中的 OP 已经使用 Box 并且需要指定具体的特征类型。这里或多或少是相反的......
    【解决方案2】:

    在这个具体的例子中,你可以通过使用 函数指针 来避免 trait 对象

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

    Lukas Kalbertodt's answer 的其余部分保持不变:

    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")));
    }
    

    这仅是有效的,因为您的闭包不捕获任何环境。因此,Rust 编译器能够将它们“提升”为完整函数,然后引用隐式函数。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-08-16
      • 1970-01-01
      • 2019-07-11
      • 1970-01-01
      • 2022-08-19
      • 2021-08-04
      • 2011-02-14
      • 1970-01-01
      相关资源
      最近更新 更多