闭包的类型别名

Posted

技术标签:

【中文标题】闭包的类型别名【英文标题】:Type aliases for closures 【发布时间】:2015-02-20 06:24:23 【问题描述】:

我原以为下面的代码会起作用:

use std::num::Num;
use std::fmt::Show;

pub type GradFn<T : Num> = for<'a> fn(&'a [T]) -> (T, Vec<T>);

fn minimize<T : Show, F>(f : GradFn<T>, x0 : &[T]) 
    // some no-op to test types
    print!("",f(x0))


fn main() 
    let xSquared : GradFn<f64> = |x : &[f64]| -> (f64, Vec<f64>) 
        return (x[0] * x[0], vec![2.0 * x[0]]);
    ;
    let (fx, grad)  = xSquared(vec![2.0f64].as_slice());
    print!("", fx);

但我遇到了编译器错误(请参阅here):

<anon>:12:32: 14:4 error: mismatched types: expected `fn(&'a [f64]) -> (f64, collections::vec::Vec<f64>)`, found `|&[f64]| -> (f64, collections::vec::Vec<f64>)` (expected extern fn, found fn)
<anon>:12   let xSquared : GradFn<f64> = |x : &[f64]| -> (f64, Vec<f64>) 
<anon>:13     return (x[0] * x[0], vec![2.0 * x[0]]);
<anon>:14   ;

【问题讨论】:

我不是这方面的专家,但我觉得对你想要达到的目标进行更多解释会对那些可能能够回答的人有所帮助。 我正在尝试为函数指针类型设置类型别名并在函数的参数中使用它。下面的答案有效,但在我的实际应用中,我将执行许多使用相同函数签名的函数,并且重复完整类型似乎很乏味。 【参考方案1】:

fn 没有定义闭包类型;它定义了裸函数指针(即指向使用 fn 关键字定义的函数的指针)。这就是为什么您不能将闭包分配给GradFn。相反,您想使用FnFnMutFnOnce

我还需要进行一些更改才能编译此代码:

minimize 上,您编写的f 参数按值接收未调整大小的类型,这是被禁止的。您还放置了一个不使用的F 类型参数。您可能打算约束 F 并使用 F 作为 f 的类型。 编译器不允许我们在类型参数约束中使用类型别名;我们需要充分说明这个特征。这意味着类型别名基本上没有用。 我删除了xSquared 上的类型注释,这是不必要的。这让我可以完全删除类型别名。

这是最终代码:

#![feature(unboxed_closures)]

use std::num::Num;
use std::fmt::Show;

fn minimize<T: Show, F: FnMut(&[T]) -> (T, Vec<T>)>(mut f: F, x0: &[T]) 
    // some no-op to test types
    print!("", f(x0))


fn main() 
    let xSquared = |x: &[f64]| -> (f64, Vec<f64>) 
        return (x[0] * x[0], vec![2.0 * x[0]]);
    ;
    let (fx, grad)  = xSquared(vec![2.0f64].as_slice());
    print!("", fx);

【讨论】:

好答案。在这种情况下,他也可以使用裸函数指针 (playpen) 有没有办法维护别名?如果我们有多个函数需要GradFn,这里的部分要点是避免重复 @AriaHaghighi:不幸的是,编译器不接受需要特征绑定的类型别名。然而,我刚刚意识到我可以让这个特征绑定更短,所以我相应地编辑了我的答案。 创建一个子特征怎么样:trait GradFn&lt;T&gt; : FnMut(&amp;[T]) -&gt; (T, Vec&lt;T&gt;) 似乎编译并具有相同的结果。 @Shepmaster:我不知道它是否是故意的;如果是这样,那肯定是一个很好的便利。如果它不是故意的并且最终被“修复”,人们仍然可以通过添加一揽子 impl 来使用这种技术(impl&lt;T, A: FnMut(&amp;[T]) -&gt; (T, Vec&lt;T&gt;)&gt; GradFn&lt;T&gt; for A ;它现在也可以工作)。【参考方案2】:

如果您的 GradFn 实际上是一个裸函数指针(而不是闭包),您可以保留类型别名,如下所示:

use std::num::Num;
use std::fmt::Show;

// this type has to point to a bare function, not a closure
pub type GradFn<T> = for<'a> fn(&'a [T]) -> (T, Vec<T>);

fn minimize<T>(f : GradFn<T>, x0 : &[T]) 
    where T: Show + Num 
    // some no-op to test types
    println!("",f(x0))


fn main() 
    // this is now a bare function
    fn x_squared(x : &[f64]) -> (f64, Vec<f64>) 
        return (x[0] * x[0], vec![2.0 * x[0]]);
    

    // and this is a pointer to it, that uses your type alias
    let x_sq : GradFn<f64> = x_squared;

    let (fx, grad)  = x_sq(&[2f64]);
    println!("fx:  - grad: ", fx, grad);

    minimize(x_sq, &[3f64]);; // works with minimize as well

【讨论】:

以上是关于闭包的类型别名的主要内容,如果未能解决你的问题,请参考以下文章

函数高级类型名别名if-else 的使用包的使用for循环swich的使用数组的使用

Typescript类型别名

C 语言数据类型本质 ( 数据类型别名 | typedef 关键字 | 为复杂数据类型设置别名 | 为简单数据类型设置别名 )

Swift typealias associatedType

如何从联合类型中解开泛型类型别名,从而使类型别名更具体?

mybatis类型别名