Async functional programming

Notice how I must use a nested block_on call:

    #[test]
    fn create_cnac() {
        block_on( async {
            load_node_nac().await.and_then(|mut node_nac| {
                println!("Loaded NAC with nid {}", node_nac.get_id());

                block_on( async { match node_nac.create_client_account(None, false,"tbraun96", "mrmoney10", "Thomas P Braun").await {
                    Ok(mut cnac) => {
                        println!("CNAC successfully constructed. Saving to hard drive");
                        let mut write = cnac.write();
                        for _ in 0..100 {
                            write.toolset.update().await.and_then(|_|{
                                //println!("Update done");
                                Ok(())
                            });
                        }
                        std::mem::drop(write);

                        match cnac.save_to_disk() {
                            Ok(_) => {
                                println!("Saved CNAC to disk successfully");
                            },
                            Err(err) => {
                                println!("ERR: {}", err.to_string());
                            }
                        }
                    },
                    Err(err) => {
                        println!("ERR: {}", err.to_string());
                    }
                }});

                Ok(())
            })
        });
    }

Of course, this panics during runtime because we can't have nested executors:

thread 'tests::create_cnac' panicked at 'cannot execute `LocalPool` executor from within another executor: EnterError'

This is me trying to work-around the error caused when I go with the naive approach:

    #[test]
    fn create_cnac() {
        block_on( async {
            // notice below how I get rid of block_on, and try to do an inner async closure
            load_node_nac().await.and_then(|mut node_nac| async {
                println!("Loaded NAC with nid {}", node_nac.get_id());

                match node_nac.create_client_account(None, false,"tbraun96", "mrmoney10", "Thomas P Braun").await {
                    Ok(mut cnac) => {
                        println!("CNAC successfully constructed. Saving to hard drive");
                        let mut write = cnac.write();
                        for _ in 0..100 {
                            write.toolset.update().await.and_then(|_|{
                                //println!("Update done");
                                Ok(())
                            });
                        }
                        std::mem::drop(write);

                        match cnac.save_to_disk() {
                            Ok(_) => {
                                println!("Saved CNAC to disk successfully");
                            },
                            Err(err) => {
                                println!("ERR: {}", err.to_string());
                            }
                        }
                    },
                    Err(err) => {
                        println!("ERR: {}", err.to_string());
                    }
                }
            })
        });
    }

And this causes the compile-time error:

'await' is only allowed inside 'async' functions and blocks

Sure, I could fire-up a ThreadPool and pass around the handle which implements Spawn + Clone, and then pass the futures into it, but I was wondering if there is a clean way of doing async functional programming?

1 Like

I'm guessing the and_then is from Result, in that case you could just use the ? operator right?

let mut node_nac = load_node_nac().await?;

// rest of your code

I could, yes. And this is how I had it before (except I used a plain unwrap()). However, it would be nice to use the functional style. If this isn't possible, then it's not a big deal. But still.

I don't think you can because you would need to pass a async closure to and_then but that isn't possible.

Might you know if there are any plans to have it work? Maybe, they can add AndThen and other lambda functions to async-std?

I think we do have async closures in nightly, but it doesn't interact well with the rest of std yet. I don't think there are any plans yet on integrating async closures with the rest of std just yet.

1 Like

That'l be a sweet feature to have stabilized here soon. Lots in store for Rust's future!

1 Like