从函数返回闭包

aoc*_*via 32 rust

注意:这个问题是在Rust第一次稳定发布之前提出的.之后发生了很多变化,函数中使用的语法甚至不再有效.尽管如此,Shepmaster的答案仍然非常出色,这使得这个问题值得保留.


最后,未装箱的封闭装置着陆了,所以我正在试验它们,看看你能做些什么.

我有这个简单的功能:

fn make_adder(a: int, b: int) -> || -> int {
    || a + b
}
Run Code Online (Sandbox Code Playgroud)

但是,我收到了一个missing lifetime specifier [E0106]错误.我试图通过将返回类型更改为修复此问题||: 'static -> int,但后来又出现了另一个错误cannot infer an appropriate lifetime due to conflicting requirements.

如果我理解正确,关闭是未装箱的,所以它拥有ab.我觉得它需要一辈子似乎很奇怪.我怎样才能解决这个问题?

She*_*ter 30

从Rust 1.26开始,您可以使用impl trait:

fn make_adder(a: i32) -> impl Fn(i32) -> i32 {
    move |b| a + b
}

fn main() {
    println!("{}", make_adder(1)(2));
}
Run Code Online (Sandbox Code Playgroud)

这允许返回未装箱的闭包,即使无法指定闭包的确切类型.

这会不会帮助你,如果任何的这些都是真的:

  1. 您在此版本之前定位Rust

  2. 你的函数有任何条件:

    fn make_adder(a: i32) -> impl Fn(i32) -> i32 {
        if a > 0 {
            move |b| a + b
        } else {
            move |b| a - b
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

    这里没有一种返回类型; 每个封口都有一种独特的,不可媲美的类型.

  3. 您需要能够出于任何原因命名返回的类型:

    struct Example<F>(F);
    
    fn make_it() -> Example<impl Fn()> {
        Example(|| println!("Hello"))
    }
    
    fn main() {
        let unnamed_type_ok = make_it();
        let named_type_bad: /* No valid type here */ = make_it();
    }
    
    Run Code Online (Sandbox Code Playgroud)

    您不能将其impl SomeTrait用作变量类型.

在这些情况下,您需要使用间接.常见的解决方案是特质对象,如另一个答案中所述.

  • @detly 是的,确实如此。 (2认同)

Vla*_*eev 25

可以在Boxes中返回闭包,也就是说,作为实现某些特征的特征对象:

fn make_adder(a: i32) -> Box<Fn(i32) -> i32> {
    Box::new(move |b| a + b)
}

fn main() {
    println!("{}", make_adder(1)(2));
}
Run Code Online (Sandbox Code Playgroud)

(在这里试试)

还有一个关于添加未装箱的抽象返回类型的RFC(它的跟踪问题),它允许按值返回闭包,没有框,但是这个RFC被推迟了.根据RFC中的讨论,似乎最近已经完成了一些工作,因此有可能相对较快地提供未装箱的抽象返回类型.


huo*_*uon 6

||语法仍然是旧盒装关闭,所以这不会为以前不一样工作的原因所在。

而且,即使使用正确的盒装闭包语法也不起作用|&:| -> int,因为它实际上只是某些特征的糖。目前,糖的语法是|X: args...| -> ret,其中X可以是&&mut或者什么都没有,对应于Fn, FnMut,FnOnce特征,你也可以Fn<(args...), ret>为非糖形式写等。糖可能会发生变化(可能类似于Fn(args...) -> ret)。

每个未装箱的闭包都有一个由编译器内部生成的唯一的、不可命名的类型:谈论未装箱的闭包的唯一方法是通过泛型和特征边界。特别是,写

fn make_adder(a: int, b: int) -> |&:| -> int {
    |&:| a + b
}
Run Code Online (Sandbox Code Playgroud)

就像写作

fn make_adder(a: int, b: int) -> Fn<(), int> {
    |&:| a + b
}
Run Code Online (Sandbox Code Playgroud)

即说make_adder返回一个未装箱的特征值;目前这没有多大意义。首先要尝试的是

fn make_adder<F: Fn<(), int>>(a: int, b: int) -> F {
    |&:| a + b
}
Run Code Online (Sandbox Code Playgroud)

但是这是说,make_adder是返回任何F主叫方选,而我们想说它返回一些固定的(但“隐藏”)类型。这需要抽象返回类型,基本上说,“返回值实现了这个特征”,同时仍然被拆箱和静态解析。用那个(暂时关闭的)RFC 的语言,

fn make_adder(a: int, b: int) -> impl Fn<(), int> {
    |&:| a + b
}
Run Code Online (Sandbox Code Playgroud)

或者用封糖。

(另一个小问题:我对未装箱的闭包不是 100% 确定,但旧的闭包肯定仍然通过引用捕获事物,这是另一个使问题中提出的代码下沉的事情。这在#16610 中得到纠正。)