Storing closure in struct -


#1

https://play.rust-lang.org/?gist=f36ac25a2e80a876b3b2b32666a2817c&version=stable

This is my latest attempt to pass around a closure through multiple nested structs.

   Compiling playground v0.0.1 (file:///playground)
error[E0277]: the trait bound `[closure@src/main.rs:71:27: 71:100]: ActionClosure` is not satisfied
  --> src/main.rs:72:15
   |
72 |     let ctx = Context::new(action, 7);
   |               ^^^^^^^^^^^^ the trait `ActionClosure` is not implemented for `[closure@src/main.rs:71:27: 71:100]`
   |
   = note: required by `<Context<A>>::new`

error[E0277]: the trait bound `[closure@src/main.rs:71:27: 71:100]: ActionClosure` is not satisfied
  --> src/main.rs:72:15
   |
72 |     let ctx = Context::new(action, 7);
   |               ^^^^^^^^^^^^^^^^^^^^^^^ the trait `ActionClosure` is not implemented for `[closure@src/main.rs:71:27: 71:100]`
   |
   = note: required by `Context`

error[E0599]: no method named `handle` found for type `Context<[closure@src/main.rs:71:27: 71:100]>` in the current scope
  --> src/main.rs:74:9
   |
74 |     ctx.handle();
   |         ^^^^^^
   |
   = note: the method `handle` exists but the following trait bounds were not satisfied:
           `[closure@src/main.rs:71:27: 71:100] : ActionClosure`

error[E0277]: the trait bound `[closure@src/main.rs:71:27: 71:100]: ActionClosure` is not satisfied
  --> src/main.rs:76:5
   |
76 |     println!("{:?}", ctx);
   |     ^^^^^^^^^^^^^^^^^^^^^^ the trait `ActionClosure` is not implemented for `[closure@src/main.rs:71:27: 71:100]`
   |
   = note: required by `Context`
   = note: this error originates in a macro outside of the current crate

error[E0277]: the trait bound `[closure@src/main.rs:71:27: 71:100]: ActionClosure` is not satisfied
  --> src/main.rs:76:22
   |
76 |     println!("{:?}", ctx);
   |                      ^^^ the trait `ActionClosure` is not implemented for `[closure@src/main.rs:71:27: 71:100]`
   |
   = note: required because of the requirements on the impl of `std::fmt::Debug` for `Context<[closure@src/main.rs:71:27: 71:100]>`
   = note: required by `std::fmt::Debug::fmt`

error: aborting due to 5 previous errors

error: Could not compile `playground`.

To learn more, run the command again with --verbose.

But I am not sure how to deal with this. I read a couple of blogs, and the boxed function seems to be the way to go, but I am still not sure how to cover my use case best.


#2

The ActionClosure is not actually implemented for any types. You probably want to add:

impl<T> ActionClosure for T where T: (Fn(&Candle) -> bool) + Sized + Sync + Clone {}

But note that closures don’t implement Clone.


#3

ActionClosure needs a blanket impl

impl<F> ActionClosure for F where F: (Fn(&Candle) -> bool) + Sized + Sync + Clone {}

Closures don’t implement Clone… yet.

Option 1: (the nightly way) You can use a nightly feature:

#![feature(clone_closures)]

Option 2: (the stable way) You can do the painful act of desugaring closures yourself. (basically, creating structs that implement traits like Fn/FnMut/FnOnce. By the way, you’ll also need to create your own versions of the Fn/FnMut/FnOnce (or at least just Fn if you don’t need the others) since you cannot impl the built-in ones on stable. Ouch)

Closures don’t implement Debug

Option 1: Make Debug impls that don’t require debuggable closures. I did that in this playground link.

(note: that sample overflows the heap when run. I assume this is due to contortions made to minimize the original code, so I haven’t bothered looking into it yet)

Option 2: Make debuggable closures. Hm, looks like I get to post this link again. Maybe I should turn it into a crate?: Make an arbitrary value Debug

Option 3: Don’t bother trying to debug them. (because now that I think about it you probably only added that println that for the purposes of example. :stuck_out_tongue:)


#4

Thanks for the long explanation and options I have.

Initially I wanted to only store references in the Cake.

The Debug impl was only there to prevent the compiler from optimizing everything away, actually I did an explicit impl for fmt::Debug - but I guess I posted an older link of that playgroud.

I am still a little confused why there is the issues with Clone for the Box<Closure> version, since I actually can clone the boxed version, right?

https://play.rust-lang.org/?gist=39e4d4d47c86978e27af0f6a473a5f95

But I might run into implications if the closure is not living long enough… how would I circumvent that?


#5

A Box is a unique pointer to a value. Cloning a Box requires cloning the value. To make an arbitrary thing clonable would require a ref-counted pointer (Rc or Arc).

https://play.rust-lang.org/?gist=39e4d4d47c86978e27af0f6a473a5f95

That snippet doesn’t seem to show that you can clone it, only that you can write #[derive(Clone)]. The compiler automatically inserts an A: Clone bound in the generated implementation.