3

I have a struct which looks something like this:

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

How do I implement Clone for this struct?

One way I found to copy the function body is:

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

But this results in mapper having a different type than that of mystruct.mapper.

playground

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
John
  • 1,856
  • 2
  • 22
  • 33
  • What is `I`? This doesn't compile. If `F` didn't return anything, though, why not just `#[derive(Clone)]`? – ljedrz Oct 01 '16 at 05:37
  • @ljedrz Ah! Sorry, fixed it... Please take a look at the playpen link that I added now. – John Oct 01 '16 at 05:51

4 Answers4

5

As of Rust 1.26.0, closures implement both Copy and Clone if all of the captured variables do:

#[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));
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
3

You can't Clone closures. The only one in a position to implement Clone for a closure is the compiler... and it doesn't. So, you're kinda stuck.

There is one way around this, though: if you have a closure with no captured variables, you can force a copy via unsafe code. That said, a simpler approach at that point is to accept a fn(usize) -> f64 instead, since they don't have a captured environment (any zero-sized closure can be rewritten as a function), and are Copy.

DK.
  • 55,277
  • 5
  • 189
  • 162
  • Thanks! Now that I think about it, I won't need to capture variables in the closure, though it would have been nicer (more readable) to use closures. Any idea why `Clone` is not implemented for closures in the compiler? – John Oct 01 '16 at 06:06
  • 1
    @John I believe that part of the issue is that, in order to implement `Clone`, the compiler has to know what `Clone` is. Historically, it hasn't. In addition, what about other traits? Should a closure also be `Copy`? `Eq`? Where do you draw the line? I don't think anyone is really sure what, *exactly*, should be done here. – DK. Oct 01 '16 at 06:09
  • 2
    `fn(usize) -> f64` (a "fn type") is not zero-sized, it's the equivalent of a function pointer. `fn(usize) -> f64 {::some::specific::function}` (a "fn item type") is zero-sized but you can't write out that type, so the only way you can make use of that is by accepting a generic type `F: Fn(u64) -> f64 + Copy` (i.e., just add a `Copy` bound to the code in the question). –  Oct 01 '16 at 08:27
  • @delnan Does that by any chance imply that "fn types" cannot be inlined, but taking an "fn" as `F: Fn(..) -> .. + Copy` could make inlining possible? – John Oct 01 '16 at 09:35
  • 1
    @John In principle, if you have a *constant* function pointer somewhere, the compiler can optimize based on the value of that constant (as with any other value). But in practice, you'll probably pass in different function pointers from different call sites, so most callbacks specified via `fn` are indeed unlikely to be inlined. –  Oct 01 '16 at 10:58
  • @delnan Sorry, I meant the size of the captured environment (they don't have one). I've adjusted the answer. – DK. Oct 01 '16 at 11:34
3

You can use Rc (or Arc!) to get multiple handles of the same unclonable value. Works well with Fn (callable through shared references) closures.

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(),
           ...
        }
    }
}

Remember that #[derive(Clone)] is a very useful recipe for Clone, but its recipe doesn't always do the right thing for the situation; this is one such case.

bluss
  • 12,472
  • 1
  • 49
  • 48
  • Apart from the reference counting part, is this as good as a "raw" closure? Would the function body get inlined? (I assume it gets inlined in "raw" closures, though I'm not sure about the necessary conditions for that.) – John Oct 01 '16 at 20:19
  • It's behind a pointer, so there's that indirection (but that's all). I guess it can be inlined fine, but you should verify. – bluss Oct 02 '16 at 00:40
0

You can use trait objects to be able to implement Сlone for your struct:

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));    
}
aSpex
  • 4,790
  • 14
  • 25
  • Yes, even though this is a valid option, I wanted to avoid dynamic dispatch. I'm writing a simulator, so I want to give the compiler every possibility of inlining the closures... – John Oct 01 '16 at 09:30
  • I think that in this case, an inlining is impossible. Although the normal function call (as suggested by @DK.) cheaper than a call of trait object – aSpex Oct 01 '16 at 10:09