闭包作为Rust结构中的类型

les*_*urp 2 generics closures rust

我想在Rust中创建这样的结构:

pub struct Struct<T, F>
    where T: Eq,
          T: Hash,
          F: Fn() -> T
{
    hashMap: HashMap<T, F>,
    value: T,
}
Run Code Online (Sandbox Code Playgroud)

我的构造函数看起来像这样:

pub fn new(init_value: T) -> Struct<T, F> {
    Struct {
        hashMap: HashMap::new(),
        value: init_state,
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,在尝试实例化类时,let a = Struct::<MyEnum>::new(MyEnum::Init);编译器会抱怨泛型需要两个参数(expected 2 type arguments, found 1)

我在这里看到这段代码有效:

fn call_with_one<F>(some_closure: F) -> i32
    where F: Fn(i32) -> i32 {

    some_closure(1)
}

let answer = call_with_one(|x| x + 2);
Run Code Online (Sandbox Code Playgroud)

我想问题来自于我在模板实例化中使用另一个泛型,但我该怎么做?

Fra*_*gné 6

Struct::new没有任何依赖的参数F,因此编译器无法推断它应该用于什么类型F.如果您稍后调用了一个使用的方法F,那么编译器将使用该信息来确定Struct具体类型.例如:

use std::hash::Hash;
use std::collections::HashMap;

pub struct Struct<T, F>
    where T: Eq,
          T: Hash,
          F: Fn() -> T,
{
    hash_map: HashMap<T, F>,
    value: T,
}

impl<T, F> Struct<T, F>
    where T: Eq,
          T: Hash,
          F: Fn() -> T,
{
    pub fn new(init_value: T) -> Struct<T, F> {
        Struct {
            hash_map: HashMap::new(),
            value: init_value,
        }
    }

    pub fn set_fn(&mut self, value: T, func: F) {
        self.hash_map.insert(value, func);
    }
}

fn main() {
    let mut a = Struct::new(0);
    a.set_fn(0, || 1); // the closure here provides the type for `F`
}
Run Code Online (Sandbox Code Playgroud)

但是这有一个问题.如果我们set_fn第二次使用不同的闭包调用:

fn main() {
    let mut a = Struct::new(0);
    a.set_fn(0, || 1);
    a.set_fn(1, || 2);
}
Run Code Online (Sandbox Code Playgroud)

然后我们得到一个编译器错误:

error[E0308]: mismatched types
  --> <anon>:33:17
   |
33 |     a.set_fn(1, || 2);
   |                 ^^^^ expected closure, found a different closure
   |
   = note: expected type `[closure@<anon>:32:17: 32:21]`
   = note:    found type `[closure@<anon>:33:17: 33:21]`
note: no two closures, even if identical, have the same type
  --> <anon>:33:17
   |
33 |     a.set_fn(1, || 2);
   |                 ^^^^
help: consider boxing your closure and/or using it as a trait object
  --> <anon>:33:17
   |
33 |     a.set_fn(1, || 2);
   |                 ^^^^
Run Code Online (Sandbox Code Playgroud)

正如编译器所提到的,每个闭包表达式都定义了一个全新的类型并对该类型求值.但是,通过定义Struct您的方式,您将强制HashMap使用相同类型的所有函数.这真的是你想要的吗?

如果你想要映射不同T类型的闭包的不同值,那么你需要使用特征对象而不是泛型,如编译器所建议的那样.如果您希望结构拥有闭包,那么您将不得不使用Box对象类型.

pub struct Struct<T>
    where T: Eq,
          T: Hash,
{
    hash_map: HashMap<T, Box<Fn() -> T + 'static>>,
    value: T,
}
Run Code Online (Sandbox Code Playgroud)

set_fn 可能看起来像这样:

pub fn set_fn<F: Fn() -> T + 'static>(&mut self, value: T, func: F) {
    self.hash_map.insert(value, Box::new(func));
}
Run Code Online (Sandbox Code Playgroud)