从函数返回闭包

问题描述 投票:30回答:3

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


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

我有这个简单的功能:

fn make_adder(a: int, b: int) -> || -> int {
    || a + b
}

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

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

rust
3个回答
27
投票

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

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

如果其中任何一个属实,这对你没有帮助:

  1. 您在此版本之前定位Rust
  2. 你的函数有任何条件: fn make_adder(a: i32) -> impl Fn(i32) -> i32 { if a > 0 { move |b| a + b } else { move |b| a - b } } 这里没有一种返回类型;每个封口都有一种独特的,不可媲美的类型。
  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(); } 你不能(yet)使用impl SomeTrait作为变量类型。

在这些情况下,您需要使用间接。常见的解决方案是特征对象,如the other answer中所述。


24
投票

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

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

fn main() {
    println!("{}", make_adder(1)(2));
}

(试试吧here

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


4
投票

||语法仍然是旧的盒装闭包,所以这不起作用,因为它以前没有。

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

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

fn make_adder(a: int, b: int) -> |&:| -> int {
    |&:| a + b
}

就像写作一样

fn make_adder(a: int, b: int) -> Fn<(), int> {
    |&:| a + b
}

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

fn make_adder<F: Fn<(), int>>(a: int, b: int) -> F {
    |&:| a + b
}

但是这就是说make_adder正在返回调用者选择的任何F,而我们想说它返回一些固定(但“隐藏”)类型。这需要abstract return types,它基本上说,“返回值实现了这个特性”,同时仍然是未装箱和静态解决的。在那个(暂时关闭的)RFC的语言中,

fn make_adder(a: int, b: int) -> impl Fn<(), int> {
    |&:| a + b
}

或者用封糖。

(另一个小问题:我不是100%肯定关于未装箱的闭包,但是旧的闭包当然仍然通过引用捕获事物,这是另一个沉淀代码的问题。这在#16610中正在纠正。)

© www.soinside.com 2019 - 2024. All rights reserved.