es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Llamando a una función asíncrona genérica con un argumento prestado (mutablemente).

Ejemplo mínimo de mi problema.

use std::future::Future;

async fn call_changer<'a, F, Fut>(changer: F)
where
    F: FnOnce(&'a mut i32) -> Fut,
    Fut: Future<output =="" ()=""> + 'a,
{
    let mut i = 0;
    changer(&mut i).await; // error 1
    dbg!(i); // error 2
}

#[tokio::main]
async fn main() {
    call_changer(|i| async move {
        *i = 100;
    })
    .await;
}

Esto provoca dos errores relacionados, consulte rust playground para obtener una salida detallada:

  1. El préstamo no vive el tiempo suficiente porque i se descarta al final del cuerpo de call_changer.
  2. i no se puede utilizar después del await porque todavía se está prestando de forma mutable.

Me sorprende un poco ambos errores, entiendo por qué el retorno Future de F necesita tener la misma duración ('a) que su préstamo (sección relevante del libro async). Sin embargo, según la misma referencia, el préstamo debería finalizar tan pronto como llamo al await en el resultado de changer, lo cual claramente no sucede o no tendría esos errores. Reestructurar este ejemplo a algo como el libro, donde la función changer no se pasa como parámetro sino que se llama directamente, funciona como se espera.

¿Qué está sucediendo aquí y puedo hacer algo al respecto? Reemplazar &mut con una construcción Rc<refcell<_>> funciona como se espera, pero si es posible, me gustaría evitar eso.</refcell<_>

Tags:  ,

Answer

  1. Avatar for davy.ai

    El problema con este código es que el préstamo mutable de i se mantiene durante toda la duración del futuro Fut, incluso después de que se haya completado la palabra clave await. Esto se debe a que el préstamo se realiza en el argumento de changer y es capturado por el futuro devuelto por changer.

    Para solucionar esto, puedes utilizar Pin<><dyn></dyn><output =="" ()=""> + 'a>> en lugar de Fut como tipo de retorno de changer, lo cual permitirá que el futuro se fije en el marco de la pila y se descarte inmediatamente después de la palabra clave await. Aquí está el código modificado:

    use std::future::Future;
    use std::pin::Pin;
    
    async fn call_changer<'a, F>(changer: F)
    where
        F: FnOnce(&'a mut i32) -> Pin<><dyn></dyn><output =="" ()=""> + 'a>>,
    {
        let mut i = 0;
        changer(&mut i).await;
        dbg!(i);
    }
    
    #[tokio::main]
    async fn main() {
        call_changer(|i| {
            Box::pin(async move {
                *i = 100;
            })
        })
        .await;
    }
    

    Esto debería solucionar los problemas de préstamo y permitir que el código se compile y se ejecute como se espera.

Comments are closed.