Try-Bind implementations

I've stumbled upon this pattern three times in three different code bases. I'll name it Try-Bind: First search for a match, then use the match.

I might be approaching this wrong.

...
let api = api.clone();

tokio::spawn(async move {
    let handlers: &[&dyn MessageHandler] = &[&Shrug, &Reqex];

    for handler in handlers {
        let handler = handler;
        if handler.test(&message).await {
            _ = handler.handle(message, api).await;
            return
        }
    }
});

Ideally I'd loop through all implementors of MessageHandler automatically, but here I listed them manually.

#[async_trait]
trait MessageHandler {
    async fn test(&self, message: &Message) -> bool;
    async fn handle(&self, message: Message, api: AsyncApi) -> Option<()>;
}
struct Shrug;
#[async_trait]
impl MessageHandler for Shrug {
    async fn test(&self, message: &Message) -> bool {
        message.text.as_ref().is_some_and(|t| t == "/shrug")
    }

    async fn handle(&self, message: Message, api: AsyncApi) -> Option<()> {
        let result_message = SendMessageParams::builder()
            .chat_id(message.chat.id)
            .text("¯\\_(ツ)_/¯")
            .build();

        if let Err(err) = api.send_message(&result_message).await {
            println!("Failed to send shrug: {err:?}");
            return None;
        }

        Some(())
    }
}
error: future cannot be sent between threads safely
   --> src/main.rs:34:29
    |
34  | ...                   tokio::spawn(async move {
    |                       ^^^^^^^^^^^^ future created by async block is not `Send`
    |
    = help: within `&dyn MessageHandler`, the trait `Sync` is not implemented for `dyn MessageHandler`
note: future is not `Send` as this value is used across an await
   --> src/main.rs:39:63
    |
37  | ...                   for handler in handlers {
    |                                      -------- has type `std::slice::Iter<'_, &dyn MessageHandler>` which is not `Send`
38  | ...                       let handler = handler;
39  | ...                       if handler.test(&message).await {
    |                                                     ^^^^^ await occurs here, with `handlers` maybe used later
note: required by a bound in `tokio::spawn`
   --> /home/jc/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.35.1/src/task/spawn.rs:166:21
    |
164 |     pub fn spawn<F>(future: F) -> JoinHandle<F::Output>
    |            ----- required by a bound in this function
165 |     where
166 |         F: Future + Send + 'static,
    |                     ^^^^ required by this bound in `spawn`


I have tried a dozen ways to shape the (in this case) MessageHandler functions.

Every dyn type is assumed to not be thread-safe unless specified otherwise. Change the type from &[&dyn MessageHandler] to &[&dyn MessageHandler + Sync].

Or, add a supertrait bound to MessageHandler:

trait MessageHandler: Sync {

These have the same effect except that the supertrait bound affects all uses of MessageHandler.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.