¿Cómo puedo poner una función asíncrona en un mapa en Rust?

11

No puedo manejar funciones asíncronas cuando escribo un enrutador asíncrono para hyper.

Este código:

use std::collections::HashMap;
use std::future::Future;

type BoxedResult<T> = Result<T, Box<dyn std::error::Error + Send + Sync>>;
type CalcFn = Box<dyn Fn(i32, i32) -> dyn Future<Output = BoxedResult<i32>>>;

async fn add(a: i32, b: i32) -> BoxedResult<i32> {
    Ok(a + b)
}

async fn sub(a: i32, b: i32) -> BoxedResult<i32> {
    Ok(a - b)
}

fn main() {
    let mut map: HashMap<&str, CalcFn> = Default::default();
    map.insert("add", Box::new(add));
    map.insert("sub", Box::new(sub));

    println!("map size: {}", map.len());
}

Genera el siguiente error del compilador:

error[E0271]: type mismatch resolving `<fn(i32, i32) -> impl std::future::Future {add} as std::ops::FnOnce<(i32, i32)>>::Output == dyn std::future::Future<Output = std::result::Result<i32, std::boxed::Box<dyn std::error::Error + std::marker::Send + std::marker::Sync>>>`
  --> src/main.rs:17:23
   |
17 |     map.insert("add", Box::new(add));
   |                       ^^^^^^^^^^^^^ expected opaque type, found trait std::future::Future
   |
   = note: expected type `impl std::future::Future`
              found type `dyn std::future::Future<Output = std::result::Result<i32, std::boxed::Box<dyn std::error::Error + std::marker::Send + std::marker::Sync>>>`
   = note: required for the cast to the object type `dyn std::ops::Fn(i32, i32) -> dyn std::future::Future<Output = std::result::Result<i32, std::boxed::Box<dyn std::error::Error + std::marker::Send + std::marker::Sync>>>`

error[E0271]: type mismatch resolving `<fn(i32, i32) -> impl std::future::Future {sub} as std::ops::FnOnce<(i32, i32)>>::Output == dyn std::future::Future<Output = std::result::Result<i32, std::boxed::Box<dyn std::error::Error + std::marker::Send + std::marker::Sync>>>`
  --> src/main.rs:18:23
   |
18 |     map.insert("sub", Box::new(sub));
   |                       ^^^^^^^^^^^^^ expected opaque type, found trait std::future::Future
   |
   = note: expected type `impl std::future::Future`
              found type `dyn std::future::Future<Output = std::result::Result<i32, std::boxed::Box<dyn std::error::Error + std::marker::Send + std::marker::Sync>>>`
   = note: required for the cast to the object type `dyn std::ops::Fn(i32, i32) -> dyn std::future::Future<Output = std::result::Result<i32, std::boxed::Box<dyn std::error::Error + std::marker::Send + std::marker::Sync>>>`

Parece que hay un conflicto entre impl Futurey dyn Future, pero no tengo idea de cómo manejarlo.

Ukonn Ra
fuente

Respuestas:

5

Esto sucede porque impl Futurees un tipo único concreto mientras que dyn Futurees un tipo abstracto. HashMapespera el tipo abstracto ya que solo puede contener instancias de un solo tipo.

Si podemos encuadrar el tipo de retorno de las funciones asíncronas, podremos agregar estos futuros en a HashMap.

Primero necesitamos cambiar el tipo de CalcFn:

type CalcFn = Box<dyn Fn(i32, i32) -> Pin<Box<dyn Future<Output = i32>>>>;

Entonces esto puede hacer el truco:

let mut map: HashMap<&str, CalcFn> = Default::default();
map.insert("add", Box::new(|a, b| Box::pin(add(a, b))));
map.insert("sub", Box::new(|a, b| Box::pin(sub(a, b))));

println!("map size: {}", map.len());

//map.get("add").unwrap()(2, 3).await

Este ejemplo completo simplificó Futureel Itemtipo, usando un en i32lugar de un Result. Consulte también el código completo de su caso .

Ömer Erden
fuente