V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
vghdjgh
V2EX  ›  问与答

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

  •  
  •   vghdjgh · 2016-12-21 12:07:24 +08:00 · 2881 次点击
    这是一个创建于 2699 天前的主题,其中的信息可能已经有所发展或是发生改变。

    函数逻辑大概像这样:

    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));
    
    4 条回复    2017-02-27 11:35:32 +08:00
    Kilerd
        1
    Kilerd  
       2016-12-21 13:05:22 +08:00 via iPhone
    我也懵。
    dogfeet
        2
    dogfeet  
       2017-02-26 23:25:49 +08:00   ❤️ 1
    声明周期标记的有问题, 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
        3
    vghdjgh  
    OP
       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
        4
    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));
    }
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2957 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 12:25 · PVG 20:25 · LAX 05:25 · JFK 08:25
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.