【问题标题】:Closures as a type in a Rust struct闭包作为 Rust 结构中的一种类型
【发布时间】:2017-04-26 05:31:34
【问题描述】:

我正在尝试在 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::&lt;MyEnum&gt;::new(MyEnum::Init); 实例化类时,编译器会抱怨泛型需要两个参数 (expected 2 type arguments, found 1)

我看到here 这个代码有效:

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

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

【问题讨论】:

    标签: generics rust closures


    【解决方案1】:

    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 的不同值映射到可能不同类型的闭包,那么您需要使用 trait 对象而不是泛型,正如编译器所建议的那样。如果您希望结构拥有闭包,则必须在对象类型周围使用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 语法。这是什么(所以我可以阅读相应的文档)?这关乎一生吗?
    • 'static 这是一个终身绑定。它限制了实现Fn() -&gt; T 的类型中借用指针的生命周期。 'static 表示该类型不能包含任何比'static 短的借用指针(完全没有借用指针的类型是可以的)。如果您需要更大的灵活性,您可以在 Struct 上引入生命周期参数 (&lt;'a, T&gt;) 并改用它。
    猜你喜欢
    • 2022-11-27
    • 2020-12-30
    • 2018-06-04
    • 1970-01-01
    • 1970-01-01
    • 2015-03-06
    • 2021-09-05
    相关资源
    最近更新 更多