可以定义泛型闭包吗?

时间:2016-01-15 15:27:02

标签: rust

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

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

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

我正在尝试做什么

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

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

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<T, MyError>。该值被强制转换为字段的类型。

我试图将.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> { ... }

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

2 个答案:

答案 0 :(得分:8)

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

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

let绑定不会被单态化,因此您不能拥有let a<T> = ...并且编译器为您生成了不同版本的a

我认为可以实现这一点的是引入更高级别的类型,这是Rust非常需要但尚未完全充实的新功能之一。它们可以让你写出类似的东西:

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

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

答案 1 :(得分: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)
    }
}