【问题标题】:How can I store a closure object in a struct?如何将闭包对象存储在结构中?
【发布时间】:2021-04-21 15:54:57
【问题描述】:

我不知道如何将闭包对象存储在结构中。闭包对象的参数和返回值是已知的。这是我的精简代码:

struct Instr<F>
    where F: Fn([i32;4],[i32;3]) -> [i32;4]
{
    name: String,
    op: F
}

fn main()
{
    // Simple example showing the difficulty:
    let tmp : Instr<Fn([i32;4],[i32;3]) -> [i32;4]> = Instr { name: "asd".to_string(), op: |a,b| a};

    // What I really want is something more like this:
    // let instrs = vec![
    //     Instr { name: "asdf", op: |a,b| a },
    //     Instr { name: "qwer", op: |a,b| a }
    // ];
}

坦率地说,我不明白这些错误是什么意思。在我看来,这很简单。闭包具有类型和已知尺寸。将其存储在相同类型的类型字段中应该很简单。对吧?

尝试按照错误消息提示添加 F: ?Sized 并不能修复“编译时大小未知”错误。

有人可以帮我正确编译吗?

error[E0277]: the size for values of type `dyn Fn([i32; 4], [i32; 3]) -> [i32; 4]` cannot be known at compilation time
  --> a.rs:11:15
   |
1  | struct Instr<F>
   |              - required by this bound in `Instr`
...
11 |     let tmp : Instr<Fn([i32;4],[i32;3]) -> [i32;4]> = Instr { name: "asd".to_string(), op: |a,b| a};
   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
   |
   = help: the trait `Sized` is not implemented for `dyn Fn([i32; 4], [i32; 3]) -> [i32; 4]`
help: you could relax the implicit `Sized` bound on `F` if it were used through indirection like `&F` or `Box<F>`
  --> a.rs:1:14
   |
1  | struct Instr<F>
   |              ^ this could be changed to `F: ?Sized`...
2  |     where F : Fn([i32;4],[i32;3]) -> [i32;4]
   |           - ...if indirection was used here: `Box<F>`
...
5  |     op : F
   |          - ...if indirection was used here: `Box<F>`

error[E0277]: the size for values of type `dyn Fn([i32; 4], [i32; 3]) -> [i32; 4]` cannot be known at compilation time
  --> a.rs:11:55
   |
1  | / struct Instr<F>
2  | |     where F : Fn([i32;4],[i32;3]) -> [i32;4]
3  | | {
4  | |     name : String,
5  | |     op : F
6  | | }
   | |_- required by `Instr`
...
11 |       let tmp : Instr<Fn([i32;4],[i32;3]) -> [i32;4]> = Instr { name: "asd".to_string(), op: |a,b| a};
   |                                                         ^^^^^ doesn't have a size known at compile-time
   |
   = help: the trait `Sized` is not implemented for `dyn Fn([i32; 4], [i32; 3]) -> [i32; 4]`

error[E0308]: mismatched types
  --> a.rs:11:92
   |
11 |     let tmp : Instr<Fn([i32;4],[i32;3]) -> [i32;4]> = Instr { name: "asd".to_string(), op: |a,b| a};
   |                                                                                            ^^^^^^^ expected trait object `dyn Fn`, found closure
   |
   = note: expected trait object `dyn Fn([i32; 4], [i32; 3]) -> [i32; 4]`
                   found closure `[closure@a.rs:11:92: 11:99]`

【问题讨论】:

    标签: generics rust closures traits trait-objects


    【解决方案1】:

    不同的闭包有不同的大小,所以你不能在结构中存储“原始闭包”或“原始特征对象”,它们必须在指针后面,所以你可以将它们放在 Box 中,如下所示:

    struct Instr {
        name: String,
        op: Box<dyn Fn([i32; 4], [i32; 3]) -> [i32; 4]>,
    }
    
    fn main() {
        let instrs = vec![
             Instr { name: "asdf".into(), op: Box::new(|a,b| a) },
             Instr { name: "qwer".into(), op: Box::new(|a,b| a) }
         ];
    }
    

    playground

    【讨论】:

    • 谢谢,这正是我想要的。我正在学习 rust,我认为在这个特定的练习中涉水太深了。
    【解决方案2】:

    接受的答案完美地解决了您的用例的解决方案,但我想澄清“未调整大小”的错误消息以及“简单示例”无法正常工作。

    Rust 非常有能力将闭包存储在问题中定义的 Instr 中,但是您的类型规范混淆了它。每个闭包的类型是匿名,你不能命名它。您尝试通过拼写 trait Fn(ARGS...) -&gt; RESULT 来指定闭包类型是错误的,因为在 Rust 中,当您使用预期类型的​​ trait 时,它指的是 trait 的动态实现,也就是 trait object。它是未调整大小的 trait 对象,必须通过引用或智能指针访问。

    所以,如果你让 Rust 推断它的类型,你可以创建一个带有任意闭包的Instr

    struct Instr<F>
        where F: Fn([i32;4],[i32;3]) -> [i32;4]
    {
        name: String,
        op: F
    }
    
    fn main()
    {
        // Simple example
        let tmp : Instr<_> = Instr { name: "asd".to_string(), op: |a,b| a};
    }
    

    但这将不允许您创建Instrs 的向量,每个向量都有不同的闭包,因为这些Instrs 将具有不同的类型。为此,您需要使用参考或Box,如接受的答案所示。

    【讨论】:

      猜你喜欢
      • 2021-09-05
      • 2015-03-06
      • 1970-01-01
      • 1970-01-01
      • 2015-06-08
      • 2010-10-01
      • 1970-01-01
      相关资源
      最近更新 更多