闭包的类型别名

6
我本以为以下代码可以工作:
```python # 代码示例 ```
但实际上它并不能正常运行。
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);
}

但是我得到了一个编译错误(见这里):

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

我不是这个领域的专家,但我认为更详细地解释一下你想要实现什么对于那些可能能够回答的人会有所帮助。 - Harry
我正在尝试设置一个类型别名到函数指针类型,并在函数的参数中使用它。下面的答案有效,但在我的实际应用中,我将会使用许多使用相同函数签名的函数,重复完整类型似乎很繁琐。 - Aria Haghighi
2个回答

7

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](http://play.rust-lang.org/?code=fn%20main%28%29%20{%0A%20%20%20%20fn%20x_squared%28x%20%3A%20%26[f64]%29%20-%3E%20%28f64%2C%20Vec%3Cf64%3E%29%20{%0A%20%20%20%20%20%20%20%20(x [0]%20%20x [0],vec![2.0%20%20x [0]])%0A%20%20%20%20}%0A%0A%20%20%20%20let%20(f,grad)%20%3D%20x_squared %28&[2.0f64]%29%3B%0A%20%20%20%20println!(“f%3A%20 {}%20-grad%3A%20 {}”%2C%20f%2C%20grad%29%3B%0A}) - Paolo Falabella
1
有没有办法保持别名?这里的重点之一是避免重复,如果我们有多个接受GradFn的函数。 - Aria Haghighi
@AriaHaghighi:不幸的是,编译器在期望特质绑定的地方不接受类型别名。然而,我刚刚意识到我可以让这个特质绑定更短,所以我相应地编辑了我的答案。 - Francis Gagné
1
创建一个子trait的想法如何:trait GradFn<T> : FnMut(&[T]) -> (T, Vec<T>) {}似乎可以编译并具有相同的结果。 - Shepmaster
1
@Shepmaster:我不知道它是否是有意的,如果是的话,那肯定是一个很好的便利。如果这不是有意为之的,而且最终被“修复”,那么仍然可以通过添加一个全局实现(impl<T, A: FnMut(&[T]) -> (T, Vec<T>)> GradFn<T> for A {}; 它现在也可以工作)来使用这个技巧。 - Francis Gagné

0
如果你的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
}

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