Issure with passing Struct which has function/closure to the Future/Stream

Hi everyone, i am working on tokio application where i need to pass a struct in to the stream fold , that struct contains a closures and/or pointers to the functions (depends). I have tried many different approaches and could not pass compiler errors like live times and so on.

I have created Gist code example of how i would like it to work (it is just and example and i would really like to get rid of Boxes and make it look nicer). Unfortunately it does not work any way (with or without Boxes) :frowning:

I would really appreciate if someone helps me with this issue and explains why i it should be that way as i have spend last 3 days trying to resolve and could not do that. Thank you very much :slight_smile:

Gist:
https://gist.github.com/goriunov/2e453b4bd93b8198c1216a12b7b95534

Also i had topic on reddit related this issue:

Futures don't execute in the place where they're written. They will execute somewhere else, in the future, long after all your variables, all your function calls, are gone.

Until async/await lands, you can't use references anywhere in futures.

  1. Put move on all closures used in futures.
  2. Make sure things can actually be moved, i.e. no types with & or <'a> or Ref. Box and Arc are fine.
1 Like

Even if i put move everywhere i get cannot move out of captured variable in an 'FnMut' error on stream fold method. Using only Boxes.

oh, I see it!

You have TWO FnMut's nested. One in for_each, and other in fold. That means the inner closure is not just one closure, it's many closures, each created for every run of for_each.

So it means that your one variable is trying to be moved into fold many times, for each for_each call.

The solution is two wrap it in Arc, and then in for_each do:

let myFns = Arc::clone(&myFns);

so that each copy of fold's closure can have its own copy of myFns.

1 Like

Thanks for helping, after adding Arc i still can not call any functions which are inside of myFns variable. I have created another gist with Arc added:

https://gist.github.com/goriunov/719a6742c7c0dd855933af141c78886f

The error is:

   |
37 |           (myFns.case1Fn.unwrap())(data);
   |            ^^^^^^^^^^^^^ cannot move out of an `Arc`

Edit:
Also i tried adding Mutex still no luck :frowning:

'cannot move out error' usually means you need as_ref() on Option before unwrapping it. Otherwise the Option tries to destroy itself.

1 Like

Thank you very much for you help :slight_smile: seems like it works alright with as_ref and but now i am getting slightly different error i believe it is related to the Send and Sync traits on future, after calling a closure i am getting error on tokio::spawn that it can not send returned future and i have type mismatch.

Instead of Box<Future<Item = String, Error = ()>> + Send + Sync> i am getting just Box<Future<Item = String, Error = ()>>> type. How do you attach Send and Sync to the boxed future ?

The latest gist:
https://gist.github.com/goriunov/3f2553cffd7232f5dbb2f5535cac322f

I really appreciate you help as i have learned quite a few things from your comments. Thanks again :slight_smile:

It's more helpful to see which types are dyn:

  1. Run rustup component add cargo-fix and cargo fix --edition
  2. add edition = "2018" to your Cargo toml,
  3. run cargo fix --edition-idioms

When you're working with an abstract type like dyn Trait, you get only what you ask for, and nothing more. Even if actual types you use it with support extra properties like Send/Sync, Rust pretends they don't exist, because you didn't ask for them to be guaranteed.

So to guarantee that every boxed future you could get is Send + Sync, you have to explicitly ask for it:

type Func = Box<dyn Fn(Vec<u8>) -> Box<dyn Future<Item = String, Error = ()> + Send + Sync> + Send + Sync>;

Note that you have two Box<dyn Trait> there, so you have to ask for Send/Sync for both.

1 Like

Everything is working alright :grinning: Thank you very much for your help :slight_smile:

1 Like