快被 rust 的所有权和生命周期搞死了, rust 怎么实现,参数是 closure、返回 closure 的高阶函数?

2016-12-21 12:07:24 +08:00
 vghdjgh

函数逻辑大概像这样:

const sum = (f, g) => x => f(x) + g(x);

目前我能按文档( https://doc.rust-lang.org/book/closures.html )做成下面这样,但是总是编译不过。

fn sum<F, G>(f: &'static F, g: &'static G) -> Box<Fn(i32) -> i32>
    where F: Fn(i32) -> i32,
            G: Fn(i32) -> i32
{
    Box::new(move |x| f(x) + g(x))
}
let f1 = |x: i32| x * x;
let f2 = |x: i32| x * 2 + 1;
let a = sum(&f1, &f2);
println!("{}", a(1i32));
println!("{}", a(2i32));
println!("{}", a(3i32));
2956 次点击
所在节点    问与答
4 条回复
Kilerd
2016-12-21 13:05:22 +08:00
我也懵。
dogfeet
2017-02-26 23:25:49 +08:00
声明周期标记的有问题, sum 中的 2 个参数都被标记为 ’ static 了,实际传入的壁报声明周期并不是 'static 的。而且返回的 Box 实际上是引用了传入的 2 个参数,实际上返回值这里有个非常绕的问题。正常 move 的 closure 会使用非引用的形式捕获,但是由于你传入的是 2 个 trait object ,所以无论怎样返回的 Box 都有生命周期的依赖。
改写了一下,下面的形式可以通过,总之, Rust 的闭包我是真心感觉蛋疼:

```rust
fn sum<'a, F, G>(f: &'a F, g: &'a G) -> Box<(Fn(i32) -> i32) + 'a>
where F: Fn(i32) -> i32,
G: Fn(i32) -> i32
{
Box::new(move |x: i32| f(x) + g(x))
}

fn main() {
let f1 = |x: i32| x * x;
let f2 = |x: i32| x * 2 + 1;
let a = sum(&f1, &f2);
println!("{}", a(1i32));
println!("{}", a(2i32));
println!("{}", a(3i32));
}
```
vghdjgh
2017-02-27 07:34:24 +08:00
@dogfeet
感谢
后来我改成全 box 的形式,也可以通过编译:

let sum = |f: Box<Fn(i32) -> i32>, g: Box<Fn(i32) -> i32>| Box::new(move |x| f(x) + g(x));
let a = sum(Box::new(|x| x * x), Box::new(|x| 2 * x + 1)); // 应该是 x * x + x * 2 + 1
println!("{}", a(1)); // 应该是 4
println!("{}", a(2)); // 应该是 9
println!("{}", a(3)); // 应该是 16
dogfeet
2017-02-27 11:35:32 +08:00
@vghdjgh 是的。 Box 也是可以的。
其实这么写挺蛋疼的,必须要用 trait object ,因为闭包的类型是匿名的。用 Map 类似的结构,如果闭包是传入的,那么传入的类型是可以推倒出来的,如果没有传入,就很麻烦了。
其实这个需求很早就有在讨论了,开启 conservative_impl_trait 就可以使用 impl trait 这种方式约束返回类型了。

#![feature(conservative_impl_trait)]

fn sum<F, G>(f: F, g: G) -> Box<(impl Fn(i32) -> i32)>
where F: Fn(i32) -> i32,
G: Fn(i32) -> i32
{
Box::new(move |x: i32| f(x) + g(x))
}

fn main() {
let f1 = |x: i32| x * x;
let f2 = |x: i32| x * 2 + 1;
let a = sum(f1, f2);
println!("{}", a(1i32));
println!("{}", a(2i32));
println!("{}", a(3i32));
}

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/329112

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX