Rust中的闭包作为结构体类型

8

我正在尝试在Rust中创建这样一个结构体:

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

我的构造函数如下:

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

然而,当尝试实例化该类时,使用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);

我猜问题出在我在模板实例化中有另一个通用类型,但我该怎么做呢?

1个回答

11

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`
}

但是这里存在一个问题。如果我们再次调用 set_fn ,并传入不同的闭包函数:

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

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

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);
   |                 ^^^^

正如编译器所述,每个闭包表达式都定义了一个全新的类型并返回该类型。然而,通过你定义Struct的方式,你强制HashMap中的所有函数具有相同的类型。这真的是你想要的吗?
如果你想将T的不同值映射到可能不同类型的闭包,则需要使用特征对象而不是泛型,正如编译器建议的那样。如果你想让结构体拥有闭包,则必须在对象类型周围使用Box
pub struct Struct<T>
    where T: Eq,
          T: Hash,
{
    hash_map: HashMap<T, Box<Fn() -> T + 'static>>,
    value: T,
}

set_fn 可以像这样:

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

谢谢!那确实解决了我的问题。不过我不理解 T + 'static 的语法。这是什么意思(这样我就可以阅读相应的文档了)?这与生命周期有关吗? - lesurp
这里的 'static 是一个 _lifetime bound_。它限制了实现 Fn() -> T 的类型中借用指针的生命周期。'static 意味着该类型不能包含比 'static 生命周期更短的任何借用指针(没有借用指针的类型是可以的)。如果需要更多的灵活性,可以在 Struct 上引入一个生命周期参数 (<'a, T>) 并使用它。 - Francis Gagné

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接