rust - ps4 - ¿Por qué intentarlo() y? no se compila cuando se usa en una función que no devuelve Opción o Resultado?
rust traduccion (4)
¿Por qué este código no se compila?
use std::{fs, path::Path};
fn main() {
let dir = Path::new("../FileSystem");
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:11:17
|
11 | for item in try!(fs::read_dir(dir)) {
| ^^^^^^^^^^^^^^^^^^^^^^^ expected (), found enum `std::result::Result`
|
= note: expected type `()`
found type `std::result::Result<_, _>`
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
También probé el operador de signo de interrogación:
for item in fs::read_dir(dir)? {
Que tuvo un error diferente:
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:11:17
|
11 | for item in fs::read_dir(dir)? {
| ^^^^^^^^^^^^^^^^^^ cannot use the `?` operator in a function that returns `()`
|
= 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
¿Debo evitar
try!()
Y
?
?
¿Cuál es la mejor manera de manejar los errores?
Principalmente 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
}
A partir de Rust 1.26, Rust admite un valor de retorno de main () y, por lo tanto, admite el uso del operador de verificación de errores
?
(o, de forma equivalente, la macro
try!()
) en
main()
cuando
main()
está definido 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 que
foo.txt
no existe en la ruta local).
El
RFC ques_in_main
se fusionó recientemente.
Una vez que se haya
completed
, la sintaxis de la pregunta se compilará perfectamente y funcionará según lo previsto, siempre que las llamadas a
try!()
Se reemplacen con
?
operador.
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 Rust Book señalan la centralidad de la función principal, si ejecuta esto dentro de ella, obtendrá un error similar. Si envuelve el código dentro de una función que maneja los errores, el fragmento mencionado anteriormente 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 pretenden ser una buena codificación de Rust :)
try!
es una macro que devuelve
Err
s automáticamente;
?
es una sintaxis que hace casi lo mismo, pero funciona con cualquier tipo que implemente el rasgo
Try
.
A partir de
Rust 1.22.0
,
Option
implementa
Try
, por lo que se puede usar con
?
.
Antes de eso
?
solo se puede usar en funciones que devuelven un
Result
.
try!
continúa trabajando solo con
Result
s.
A partir de
Rust 1.26.0
,
main
puede devolver un valor que implementa la
Termination
.
Antes de eso, no devuelve ningún valor.
A partir de Rust 1.26.0
Su código original funciona si marca
main
como que devuelve un
Result
y luego devuelve
Ok(())
en todos los casos de "éxito":
use std::{fs, io, path::Path};
fn main() -> Result<(), io::Error> {
let dir = Path::new("../FileSystem");
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 podría transformar su código para usarlo
?
:
use std::{error::Error, fs, path::Path};
fn print_dir_contents() -> Result<String, Box<Error>> {
let dir = Path::new("../FileSystem");
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()),
}
}
Aquí hay una gran cantidad de manejo de errores que no puede esperar, ¡otros idiomas no suelen requerirlo! Pero existen en otros idiomas: Rust solo te hace saberlo. Aquí están los errores:
entry?
Los errores de E / S 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()
También puede
to_str
y lanzar un error, pero es mejor hacerlo.
Este error existe porque no todos los nombres de archivo son válidos Unicode.
try!
y
?
arroje errores en el valor de retorno, convirtiéndolos en
Box::Error
.
En realidad, es más razonable devolver un error amalgamado de todas las cosas que pueden salir mal.
Afortunadamente
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, esta comprobación ya está en
fs::read_dir
, por lo que en realidad solo puede eliminar el
if !dis.is_dir
completo:
use std::{fs, io, path::Path};
fn print_dir_contents() -> Result<String, io::Error> {
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()),
}
}