Pregunta ¿Por qué probar! () Y? no compilar cuando se usa en main?


¿Por qué este código no se compila?

use std::io;
use std::fs;
use std::path::Path;

fn main() {
    // Open path
    let dir = Path::new("../FileSystem");

    // Check if it is a directory
    if !dir.is_dir() {
        println!("Is not a directory");
        return;
    }

    for item in try!(fs::read_dir(dir)) {
        let file = match item {
            Err(e) => {
                println!("Error: {}", e);
                return;
            }
            Ok(f) => f,
        };

        println!("");
    }

    println!("Done");
}

Este es el error que obtengo

error[E0308]: mismatched types
  --> src/main.rs:15:17
   |
15 |     for item in try!(fs::read_dir(dir)) {
   |                 ^^^^^^^^^^^^^^^^^^^^^^^ expected (), found enum `std::result::Result`
   |
   = note: expected type `()`
              found type `std::result::Result<_, _>`
   = help: here are some functions which might fulfill your needs:
           - .unwrap()
           - .unwrap_err()
           - .unwrap_or_default()
   = note: this error originates in a macro outside of the current crate

Creo que se está quejando de esta línea: for item in try!(fs::read_dir(dir)) 

También probé con el operador de signo de interrogación:

for item in fs::read_dir(dir)? {

Que tenía un error diferente:

error[E0277]: the trait bound `(): std::ops::Try` is not satisfied
  --> src/main.rs:15:17
   |
15 |     for item in fs::read_dir(dir)? {
   |                 ------------------
   |                 |
   |                 the `?` operator can only be used in a function that returns `Result` (or another type that implements `std::ops::Try`)
   |                 in this macro invocation
   |
   = help: the trait `std::ops::Try` is not implemented for `()`
   = note: required by `std::ops::Try::from_error`

Las versiones anteriores de Rust tenían un error similar sobre std::ops::Carrier

Debería evitar try!() y ?? ¿Cuál es la mejor manera de manejar los errores? Mayormente lo hago así:

match error_prone {
    Err(e) => {
        println!("Error: {}", e);
        return;
    },
    Ok(f) => f,
};

Pero si tengo que usar eso en un bucle for, es un completo desastre

for i in match error_prone {
    // match code
} {
    // loop code
}

32
2018-05-31 08:42


origen


Respuestas:


try! es una macro que regresa Errs automáticamente; ? es la sintaxis que hace más o menos lo mismo.

A partir de Rust 1.22.0, el ? operador también se puede utilizar con un Option. Antes de eso, eso y try! solo se puede usar en funciones que devuelven un Result

A partir de Rust 1.26.0, main se le permite devolver un valor que implemente Termination. Antes de eso, no devuelve ningún valor.

A partir de Rust 1.26.0

Tu código original funciona si marcas main como devolver un Result y luego regresa Ok(()) en todos los casos de "éxito":

use std::fs;
use std::io;
use std::path::Path;

fn main() -> Result<(), io::Error> {
    // Open path
    let dir = Path::new("../FileSystem");

    // Check if it is a directory
    if !dir.is_dir() {
        println!("Is not a directory");
        return Ok(());
    }

    for item in fs::read_dir(dir)? {
        let _file = match item {
            Err(e) => {
                println!("Error: {}", e);
                return Ok(());
            }
            Ok(f) => f,
        };

        println!("");
    }

    println!("Done");
    Ok(())
}

Antes de que

Así es como puedes transformar tu código para usar ?:

use std::error::Error;
use std::fs;
use std::path::Path;

fn print_dir_contents() -> Result<String, Box<Error>> {
    // Open path
    let dir = Path::new("../FileSystem");

    // Check if it is a directory
    if !dir.is_dir() {
        return Err(Box::from("Is not a directory!"));
    }

    for entry in fs::read_dir(dir)? {
        let path = entry?.path();
        let file_name = path.file_name().unwrap();
        println!("{}", file_name.to_string_lossy());
    }

    Ok("Done".into())
}

fn main() {
    match print_dir_contents() {
        Ok(s) => println!("{}", s),
        Err(e) => println!("Error: {}", e.to_string()),
    }
}

Hay un montón de manejo de errores aquí que podría no esperar, ¡otros lenguajes no suelen requerirlo! Pero existen en otros idiomas: Rust solo hace que lo sepas. Aquí están los errores:

entry?

Los errores IO pueden ocurrir durante la iteración.

path.file_name().unwrap()

No todas las rutas tienen nombres de archivo. Podemos unwrap esto porque read_dir no nos dará una ruta sin un nombre de archivo.

file_name.to_string_lossy()

Tú también puedes to_str y arrojar un error, pero es mejor hacer esto. Este error existe porque no todos los nombres de archivos son Unicode válidos.

try! y ? arrojar errores en el valor de retorno, convirtiéndolos a Box::Error. En realidad, es más razonable devolver un error amalgamado de todas las cosas que pueden salir mal. Por suerte io::Error es el tipo correcto:

use std::io;
...
fn print_dir_contents() -> Result<String, io::Error> {
    ...
    if !dir.is_dir() {
        return Err(io::Error::new(io::ErrorKind::Other, "Is not a directory!"));
    }
    ...
}

Francamente, sin embargo, este cheque ya está en fs::read_dir, así que puedes simplemente eliminar el if !dis.is_dir en total:     use std :: fs;     use std :: io;     use std :: path :: ruta de acceso;

fn print_dir_contents() -> Result<String, io::Error> {
    // Open path
    let dir = Path::new("../FileSystem");

    for entry in fs::read_dir(dir)? {
        let path = entry?.path();
        let file_name = path.file_name().unwrap();
        println!("{}", file_name.to_string_lossy());
    }

    Ok("Done".into())
}

fn main() {
    match print_dir_contents() {
        Ok(s) => println!("{}", s),
        Err(e) => println!("Error: {}", e.to_string()),
    }
}

35
2018-05-31 10:04



los ques_in_main RFC se fusionó recientemente. Una vez que es terminado, la sintaxis en la pregunta se compilará realmente bien y funcionará según lo previsto, siempre que try!() las llamadas se reemplazan con ? operador.


5
2017-07-27 16:32



A partir de Rust 1.26, Rust admite un valor de retorno de main () y, por lo tanto, admite el uso del operador de comprobación de errores. ? (o de manera equivalente el try!() macro) en main() cuando main() se define para devolver un Result:

extern crate failure;
use failure::Error;
use std::fs::File;

type Result<T> = std::result::Result<T, Error>;

fn main() -> Result<()> {
    let mut _file = File::open("foo.txt")?; // does not exist; returns error
    println!("the file is open!");
    Ok(())
}

Lo anterior compila y devuelve un error de archivo no encontrado (asumiendo foo.txt no existe en la ruta local).

Ejemplo de patio de recreo


1
2018-05-07 17:33



La respuesta de Veedrac también me ayudó, aunque la pregunta del OP es ligeramente diferente. Mientras leía la documentación de Rust, vi este fragmento:

use std::fs::File;
use std::io::prelude::*;

let mut file = File::open("foo.txt")?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
assert_eq!(contents, "Hello, world!");

Aunque en el libro de Rust señalan la centralidad de la función principal, si ejecutas esto dentro, obtendrás un error similar. Si ajusta el código dentro de una función que maneja los errores, el fragmento antes mencionado funciona:

use std::error::Error;
use std::io::prelude::*;
use std::fs::File;

fn print_file_content() -> Result<String, Box<Error>> {
    let mut f = File::open("foo.txt")?;
    let mut contents = String::new();

    f.read_to_string(&mut contents)?;

    println!("The content: {:?}", contents);

    Ok("Done".into())
}

fn main() {
    match print_file_content() {
        Ok(s) => println!("{}", s),
        Err(e) => println!("Error: {}", e.to_string()),
    }
}

PD Estoy aprendiendo Rust, por lo que estos fragmentos no están diseñados como una buena codificación de Rust :)


0
2017-12-08 11:27