【问题标题】:Possible to define generic closure?可以定义通用闭包吗?
【发布时间】:2016-01-15 15:27:01
【问题描述】:

Rust 是否支持具有通用返回类型的闭包?例如,我想写这样的东西:

let get<T: FromValue> = |s: &str| -> Option<T> { ... }

但这种语法显然是错误的。

我要做什么

我正在使用rust-mysql-simple,我正在为我的User 结构编写from_row 方法,以从数据库行构建用户。

该库不提供(据我所知)按列名查找查询结果行值的方法。所以要解决这个问题,我的方法看起来像(编译并正常工作):

fn from_row(result: &QueryResult, row: Vec<Value>) -> User {

    let mut map: HashMap<_, _> = row.into_iter().enumerate().collect();

    let mut get = |s: &str|  {
        result.column_index(s)
           .and_then(|i| map.remove(&i) )
    };

    User {
        id: get("id").and_then(|x| from_value_opt(x).ok() )
    }
}

这里,result 是一个对象,其中包含有关查询列名的信息(用于查找列名的列索引),row 包含查询结果行中的排序值。 from_value_opt 是库提供的一种方法,它接受 Value 并返回 Result&lt;T, MyError&gt;。该值被强制转换为字段的类型。

我试图将.and_then(|x| from_value_opt(x).ok() ) 移动到get 闭包中,只是为了清理一些代码。但是,当我这样做时,闭包返回类型被解释为第一次出现 get 调用的结果。

我将闭包重写为嵌套方法,如下所示:

fn get<T: FromValue>(r: &QueryResult, m: &mut HashMap<usize, Value>, s: &str)
    -> Option<T> { ... }

这也很好,但没有帮助减少冗长。

【问题讨论】:

  • 谢谢,我添加了一些关于我想要做什么的信息。

标签: rust


【解决方案1】:

不,AFAIK 你不能。我的意思是,你可以定义一个泛型闭包,你不能做的是用泛型左侧创建一个 let 绑定。​​

fn get&lt;T&gt;,正如你提到的重写,经历了单态化,即在编译它时,rustc 为每个用于调用它的实际T 生成一个不同版本的get。当您分配 get (let a = get(...)) 的结果时,该结果具有具体的类型和大小。

let 绑定不会单态化,因此您不能拥有 let a&lt;T&gt; = ... 并拥有由编译器为您生成的不同版本的 a

我认为可能实现这一点的是引入更高种类的类型,这是 Rust 非常需要但尚未完全充实的新功能之一。它们将使您能够编写如下内容:

// does not work as of Rust 1
let a = for<T> |s: &str, t: T| {...}

即返回一个闭包,我稍后可以用 T 参数化它(这就是你所要求的)。

【讨论】:

  • 谢谢!这很有道理。
  • @kardeiz 不知道如何定义 User 以及 from_value_opt 返回什么很难说,但您可能实际上不需要 get 是通用的,您可以通过提示返回来帮助编译器键入 from_value_opt::(x)
  • 是的,这行得通,但我认为这对我没有帮助,因为我的 User 字段有不同的类型。我只是尝试将from_value_opt 调用放入闭包中并像get::&lt;Option&lt;i64&gt;&gt;("id") 一样调用闭包,但显然我无法将类型参数传递给闭包。
【解决方案2】:

闭包类型是匿名的,所以你写不出来,而且编译器似乎也无法推断,所以运气不好。

但是你有什么特别的原因要使用闭包吗?如果我正确理解了您的问题,您只需使用此功能来分解一些重复的操作,而实际上您并不会传递它。因此,内部fn 应该可以正常工作。缺点是您必须传递过去由闭包自动捕获的所有值。

应该是这样的(这个例子还是很复杂的,所以我没有尝试编译它):

fn from_row(result: &QueryResult, row: Vec<Value>) -> User {
    let mut map: HashMap<_, _> = row.into_iter().enumerate().collect();

    fn get<T: FromValue>(s: &str, result: &QueryResult, map: &mut HashMap<_, _>)
       -> T {
        result.column_index(s)
            .and_then(|i| map.remove(&i))
            .and_then(|x| from_value_opt(x)).ok()
    };

    User {
        id: get("id", &result, &mut map)
    }
}

【讨论】:

  • 感谢您的回答。是的,这就是我想使用闭包的原因——这样我就不必传递这些值了。但是,这就是我最终要走的路(请参阅我的问题的最后一部分)。
猜你喜欢
  • 2016-10-22
  • 2011-04-03
  • 1970-01-01
  • 2012-06-29
  • 2019-11-29
  • 2017-04-24
  • 1970-01-01
  • 2014-09-13
  • 2012-05-27
相关资源
最近更新 更多