【问题标题】:How do I clone a closure, so that their types are the same?如何克隆一个闭包,以便它们的类型相同?
【发布时间】:2017-02-09 17:21:26
【问题描述】:

我有一个看起来像这样的结构:

pub struct MyStruct<F>
where
    F: Fn(usize) -> f64,
{
    field: usize,
    mapper: F,
    // fields omitted
}

如何为这个结构实现Clone

我发现复制函数体的一种方法是:

let mapper = |x| (mystruct.mapper)(x);

但这会导致mapper 的类型与mystruct.mapper 的类型不同。

playground

【问题讨论】:

  • 什么是I?这不编译。但是,如果 F 没有返回任何内容,为什么不只返回 #[derive(Clone)]
  • @ljedrz 啊!抱歉,已修复...请查看我现在添加的游戏围栏链接。

标签: rust closures


【解决方案1】:

从 Rust 1.26.0 开始,闭包同时实现 CopyClone 如果所有捕获的变量都实现:

#[derive(Clone)]
pub struct MyStruct<F>
where
    F: Fn(usize) -> f64,
{
    field: usize,
    mapper: F,
}

fn main() {
    let f = MyStruct {
        field: 34,
        mapper: |x| x as f64,
    };
    let g = f.clone();
    println!("{}", (g.mapper)(3));
}

【讨论】:

    【解决方案2】:

    你不能Clone 闭包。唯一能够为闭包实现Clone 的是编译器……它没有。所以,你有点卡住了。

    one 方法解决这个问题:如果您有一个带有 no 捕获变量的闭包,您可以通过 unsafe 代码强制复制。也就是说,那个点的更简单的方法是接受fn(usize) -&gt; f64 而不是,因为它们没有捕获的环境(任何零大小的闭包都可以重写作为函数),并且是Copy

    【讨论】:

    • 谢谢!现在我想了想,我不需要在闭包中捕获变量,尽管使用闭包会更好(更具可读性)。知道为什么在编译器中没有为闭包实现Clone 吗?
    • @John 我相信部分问题在于,为了实现Clone,编译器必须知道Clone 是什么。从历史上看,它没有。此外,其他特征呢?闭包也应该是Copy 吗? Eq?你在哪里画线?我认为没有人真正确定究竟应该在这里做什么。
    • fn(usize) -&gt; f64(“fn 类型”)不是零大小的,它相当于函数指针。 fn(usize) -&gt; f64 {::some::specific::function}(“fn 项目类型”)的大小为零,但您不能写出该类型,因此您可以使用它的唯一方法是接受泛型类型 F: Fn(u64) -&gt; f64 + Copy(即,只需添加一个Copy 绑定到问题中的代码)。
    • @delnan 这是否意味着“fn types”不能被内联,但是将“fn”作为F: Fn(..) -&gt; .. + Copy 可以使内联成为可能?
    • @John 原则上,如果您在某处有一个 constant 函数指针,编译器可以根据该常量的值进行优化(与任何其他值一样)。但实际上,您可能会从不同的调用站点传递不同的函数指针,因此大多数通过fn 指定的回调确实不太可能内联。
    【解决方案3】:

    您可以使用Rc(或Arc!)来获取具有相同不可克隆值的多个句柄。与Fn(可通过共享引用调用)闭包很好地配合。

    pub struct MyStruct<F> where F: Fn(usize) -> f64 {
        field: usize,
        mapper: Rc<F>,
        // fields omitted
    }
    
    impl<F> Clone for MyStruct<F>
        where F: Fn(usize) -> f64,
    {
        fn clone(&self) -> Self {
            MyStruct {
                field: self.field,
                mapper: self.mapper.clone(),
               ...
            }
        }
    }
    

    请记住,#[derive(Clone)] 是一个非常有用的克隆配方,但它的配方并不总是适合这种情况;这是一种情况。

    【讨论】:

    • 除了引用计数部分,这和“原始”闭包一样好吗?函数体会内联吗? (我假设它被内联在“原始”闭包中,尽管我不确定这样做的必要条件。)
    • 它在指针后面,所以有间接性(但仅此而已)。我想它可以很好地内联,但你应该验证。
    【解决方案4】:

    您可以使用trait objects 来为您的结构实现Сlone

    use std::rc::Rc;
    
    #[derive(Clone)]
    pub struct MyStructRef<'f>  {
        field: usize,
        mapper: &'f Fn(usize) -> f64,
    }
    
    
    #[derive(Clone)]
    pub struct MyStructRc  {
        field: usize,
        mapper: Rc<Fn(usize) -> f64>,
    }
    
    fn main() {
        //ref
        let closure = |x| x as f64;
        let f = MyStructRef { field: 34, mapper: &closure };
        let g = f.clone();
        println!("{}", (f.mapper)(3));
        println!("{}", (g.mapper)(3));
    
        //Rc
        let rcf = MyStructRc { field: 34, mapper: Rc::new(|x| x as f64 * 2.0) };
        let rcg = rcf.clone();
        println!("{}", (rcf.mapper)(3));
        println!("{}", (rcg.mapper)(3));    
    }
    

    【讨论】:

    • 是的,即使这是一个有效的选项,我也想避免动态调度。我正在编写一个模拟器,所以我想为编译器提供内联闭包的所有可能性......
    • 我认为在这种情况下,内联是不可能的。虽然普通的函数调用(如@DK 所建议的那样)比调用 trait 对象便宜
    猜你喜欢
    • 2022-12-13
    • 1970-01-01
    • 1970-01-01
    • 2014-02-18
    • 2015-03-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多