预计关闭,发现不同的关闭

rap*_*2-h 4 rust

A是一个包含向量的结构B.A实现add_bB实例添加到列表中的方法B.B包含一个闭包属性f.

如果我B在向量中添加一个add_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")));
}
Run Code Online (Sandbox Code Playgroud)

此代码导致:

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

我怎么可以添加多个B具有各自不同的闭包Ab_vec

Luk*_*odt 7

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

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
Run Code Online (Sandbox Code Playgroud)

特别有帮助:

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

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

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

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

  • Vec<&Fn()> (对特征对象的引用)
  • Vec<Box<Fn()>> (框中的特征对象)

在你的例子中,你想要拥有所有的闭包,因此正确的选择是将所有闭包装置,就像Box<T>一个拥有的包装器,而引用只是借用东西.

一个完整的例子:

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")));
}
Run Code Online (Sandbox Code Playgroud)

  • @ rap-2-h实际上,您必须编写`Vec &lt;Box &lt;Fn()&gt;&gt;`而不是`F`。那很重要 这样,我们确实删除了所有类型参数。这是一个有效的版本:http://play.integer32.com/?gist=b387fb2439171547470e0047ec0f8f14(这在`add_b`之外进行装箱;您可以在其中通过向函数中添加类型参数而不是在`A`中进行装箱。 ) (2认同)

She*_*ter 6

在这个特定的示例中,您可以通过使用函数指针来避免特征对象

struct B {
    f: fn(),
}

impl B {
    fn new(f: fn()) -> B {
        B { f: f }
    }
}
Run Code Online (Sandbox Code Playgroud)

Lukas Kalbertodt 的其余答案保持不变:

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")));
}
Run Code Online (Sandbox Code Playgroud)

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