Problema al hacer una llamada a una función async en Rust

Publicado por Brisa
hace 1 año

Hola amigos del foro, ¡espero que estén teniendo un buen día!

Estoy teniendo un problema al intentar hacer una llamada a una función async en Rust. Estoy intentando leer un archivo y luego llamar a una función que procese ese archivo de forma asíncrona. Aquí está el código que tengo hasta ahora:

use std::fs::File;
use std::io::Read;
use tokio::runtime::Runtime;
use tokio::task;

async fn process_file(file_content: String) {
    // Aquí estaría mi lógica para procesar el contenido del archivo
    println!("Procesando el archivo: {}", file_content);
}

fn read_file(filename: &str) -> Result<String, std::io::Error> {
    let mut file = File::open(filename)?;
    let mut contents = String::new();
    file.read_to_string(&mut contents)?;

    Ok(contents)
}

fn main() {
    let filename = "archivo.txt";
    let file_content = read_file(filename).expect("Error al leer el archivo");

    let mut rt = Runtime::new().unwrap();

    rt.block_on(async {
        task::spawn(async { process_file(file_content).await; }).await.unwrap();
    });
}

Al ejecutar este código, obtengo este mensaje de error:

error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements std::ops::Try)
  --> src/main.rs:20:5
   |
20 |     file.read_to_string(&mut contents)?;
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot use the `?` operator in a function that returns `()`

error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements std::ops::Try)
  --> src/main.rs:12:43
   |
12 | fn read_file(filename: &str) -> Result<String, std::io::Error> {
   |                                           ^^^^^^^^^^^^^^^^^^^^
   |                                           |
   |                                           cannot use the `?` operator in a function that returns `()`
   |                                           returns a value but is expected to return `Result<String, std::io::Error>`

error: aborting due to 2 previous errors

Some errors have detailed explanations: E0277, E0308.
For more information about an error, try `rustc --explain E0277`.
error: could not compile `async-await` due to previous error

No puedo entender por qué estoy obteniendo este error. Parece que el operador "?" solo se puede usar en funciones que devuelven Result o Option, pero mi función read_file está devolviendo un Result.

¿Alguien puede ayudarme a entender qué estoy haciendo mal y cómo puedo solucionar esto?

¡Gracias de antemano!

rust async
Respuesta de Gordon Shumway
hace 1 año

¡Hola Brisa! Parece que hay un poco de confusión con los mensajes de error que proporcionaste y el código en sí mismo. El error E0277 indica que estás intentando usar el operador ? en un contexto que no retorna Result o Option, sin embargo, en la función read_file pareces estar utilizando el operador ? correctamente, ya que sí retorna un Result.

Tal vez el error sea parte de otra sección del código que no nos has mostrado, o podrías haber copiado incorrectamente el mensaje de error.

Dejando eso de lado, veo otro problema en tu función process_file. Esta función es async, pero no retorna un Future, en cambio, retorna () que es la unidad en Rust. Cuando defines una función async, se espera que retorne un tipo que implemente Future.

Por otro lado, el uso de tokio::task::spawn ya devuelve JoinHandle, por lo que no necesitas llamar a await dentro de la expresión de spawn. Aquí te dejo una versión corregida de tu función main:

fn main() {
    let filename = "archivo.txt";

    // Es preferible manejar el error de una forma más controlada.
    let file_content = match read_file(filename) {
        Ok(content) => content,
        Err(e) => {
            eprintln!("Error al leer el archivo: {}", e);
            return;
        },
    };

    let rt = Runtime::new().unwrap(); // Puedes hacer `Runtime::new()?;` si main() retorna Result.

    rt.block_on(async {
        // No es necesario el `await` dentro de spawn.
        // Además, deberías manejar el resultado de `.await` al final.
        let handle = task::spawn(async move {
            process_file(file_content).await;
        });

        // Aquí manejas el resultado de `JoinHandle`.
        match handle.await {
            Ok(_) => println!("El archivo fue procesado con éxito"),
            Err(e) => eprintln!("Error al procesar el archivo: {:?}", e),
        }
    });
}

Además, para que tu función process_file sea completamente correcta, deberías especificar el tipo de futuro que retorna. Como no estás devolviendo nada más que la unidad (), puedes dejar las cosas como están ahora.

La definición de tu función async sería algo como esto:

async fn process_file(file_content: String) {
    // Aquí estaría mi lógica para procesar el contenido del archivo
    println!("Procesando el archivo: {}", file_content);
    // Aquí puedes realizar más operaciones asíncronas si fuera necesario.
}

Si sigues teniendo problemas, podría ser útil correr rustc --explain E0277 para obtener una explicación detallada sobre este error y ver si hay algo que se está pasando por alto. Y asegúrate de que el error está realmente en la parte del código que nos mostraste. ¡Espero haber sido de ayuda!