Pourquoi ce code ne compile-t-il pas?
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");
}
C'est l'erreur que je reçois
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)
J'ai aussi essayé l'opérateur de point d'interrogation:
for item in fs::read_dir(dir)? {
Qui a eu une erreur différente:
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`
Les versions précédentes de Rust présentaient une erreur similaire à propos de std::ops::Carrier
Devrais-je éviter try!()
et ?
? Quel est le meilleur moyen de gérer les erreurs? Surtout je le fais comme ça:
match error_prone {
Err(e) => {
println!("Error: {}", e);
return;
},
Ok(f) => f,
};
Mais si je dois utiliser cela dans une boucle for
, c'est un désordre complet
for i in match error_prone {
// match code
} {
// loop code
}
try!
est une macro qui renvoie Err
s automatiquement; ?
est une syntaxe qui fait essentiellement la même chose, mais fonctionne avec tout type implémentant le Try
trait.
À partir de Rust 1.22.0 , Option
implémente Try
, afin qu'il puisse être utilisé avec ?
. Avant cela, ?
ne pouvait être utilisé que dans des fonctions renvoyant une Result
. try!
continue à fonctionner uniquement avec Result
s.
À partir de Rust 1.26.0 , main
est autorisé à renvoyer une valeur qui implémente Termination
. Avant cela, il ne renvoie aucune valeur.
Votre code d'origine fonctionne si vous marquez main
comme retournant une Result
et que vous renvoyez ensuite Ok(())
dans tous les "succès"
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(())
}
Voici comment vous pourriez transformer votre code pour utiliser ?
:
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()),
}
}
Vous risquez de ne pas vous attendre à beaucoup de gestion d'erreur ici - les autres langues ne l'exigent généralement pas! Mais ils existent dans d'autres langues - Rust vous le fait savoir. Voici les erreurs:
entry?
Des erreurs d'entrée-sortie peuvent survenir pendant l'itération.
path.file_name().unwrap()
Tous les chemins n'ont pas de nom de fichier. Nous pouvons unwrap
ceci car read_dir
ne nous donnera pas de chemin sans nom de fichier.
file_name.to_string_lossy()
Vous pouvez également to_str
et générer une erreur, mais il est préférable de le faire. Cette erreur existe car tous les noms de fichiers ne sont pas Unicode valide.
try!
et ?
jettent les erreurs dans la valeur de retour, en les convertissant en Box::Error
. En fait, il est plus raisonnable de renvoyer une erreur fusionnée de toutes les erreurs possibles. Heureusement, io::Error
est le bon type:
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!"));
}
// ...
}
Franchement, cependant, cette vérification est déjà dans fs::read_dir
, vous pouvez donc supprimer le if !dis.is_dir
au total:
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()),
}
}
Le ques_in_main RFC a été fusionné récemment. Une fois que complete , la syntaxe de la question sera parfaitement compilée et fonctionnera comme prévu, à condition que les appels try!()
soient remplacés par l'opérateur ?
.
À partir de Rust 1.26, Rust prend en charge une valeur renvoyée par main (), ainsi que l'utilisation de l'opérateur de contrôle d'erreur ?
(ou la macro try!()
de manière équivalente) dans main()
lorsque main()
est défini pour renvoyer 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(())
}
Ce qui précède compile et renvoie une erreur de fichier introuvable (en supposant que foo.txt
n'existe pas dans le chemin local).
La réponse de Veedrac m'a aussi aidé, bien que la question du PO soit légèrement différente. En lisant la documentation de Rust, j'ai vu cet extrait:
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!");
Bien que, dans le Rust Book, ils soulignent la centralité de la fonction principale, si vous l'exécutez à l'intérieur, vous obtiendrez une erreur similaire. Si vous intégrez le code dans une fonction traitant les erreurs, l'extrait de code susmentionné fonctionne:
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()),
}
}
P.S. J'apprends la rouille pour que ces extraits ne soient pas conçus pour être un bon code de rouille :)