Creating and passing configured closures/futures

It's difficult to make a mindset shift from javascript to rust :). In javascript, closures can share the context of a parent. I use this feature to create configured functions on the fly. I pass these configured closures/functions/promises to other structs and use them in loops. I can then just call the closure anywhere to dynamically retrieve a value. Achieving similar behavior in Rust always feels extremely painful :). Pins, boxes, arcs, etc.

Here's an example (using async-std again):

let users = Arc::new({
    let mut users: HashMap<String, String> = HashMap::with_hasher(RandomState::new());
    users.insert("John".to_string(), "John Smith".to_string());
    users
});

let get_full_name = Box::pin(|name: String| async move {
    let users = users.clone();
    users.get(&name).unwrap().to_string()
});

loop {
    let user = get_full_name("John".to_string()).await; // move occurs
    println!("{}", user);
    
    break;
}

What's the rust way to achieve this? So creating a resolver within a loop is not performant so I wanna create a configured resolver once and then call it on several places. It feels like such practice is not meant to be used in Rust. Am I right? What's the proper way?

Your example with users doesn't really need async/await. The body of your get_full_name doesn't have any .await, so it would be equivalent to an ordinary closure:

let get_full_name = |name: String| {
    let users = users.clone();
    users.get(&name).unwrap().to_string()
};

That said, your example is somewhat silly. How about getting the name directly?

loop {
    let user = users.get("John").unwrap().to_string();
    println!("{}", user);
}

True, I made a simple example using async because actual examples would be too long. So the example tries to present the problem I have. Let me try to better illustrate the use case:

...
loop {
    let router = Router::new();
    router.set_resolver(get_full_name); // => this function is called as `get_full_name("John")`
    router.resolve().await;
}

So let say you have a crate which provides a Router. You can configure this object by passing configured resolvers that retrieve some data. How these data are resolved is the responsibility of a closure you pass to it. Makes sense?

I would not use a closure for this. If you have to give it a handle like that, consider giving it the sender of an oneshot channel and await the channel instead of (or after) the router.

1 Like

Thanks. Let me think about this.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.