In Rust, I want to remove a file. The documentation tells me that fs::remove_file returns an error if the file is a directory, does not exist or the user lacks the permissions for the action.
Now, in my scenario, I would like to distinguish between those three cases. However, my debugger doesn't show me the type of the error and a println! results in simply printing the error message.
So, how could I distinguish between those cases?
use std::fs;
fn rem(path: &str) {
let msg = match fs::remove_file(path) {
Ok(()) => "is fine",
Err(NotExist) => "does not exist", // TODO: Pattern
Err(IsDir) => "is a directory", // TODO: Pattern
Err(_) => "is not yours",
};
println!("The file {}!", msg);
}
fn main() {
rem("/tmp/this-file-hopefully-not-exists.xyz"); // print "not exist"
rem("/tmp/this-is-a-directory"); // prints "is a directory"
rem("/tmp/this-file-belongs-to-root.xyz"); // prints "is not yours"
}
std::fs::remove_file returns a std::io::Result<()>, which is just an alias for Result<(), std::io::Error>.
So you can match on this to extract the type of the error message. In particular, you're probably going to want to look at the .kind() of the error:
fn rem(path: &str) {
use std::io::ErrorKind;
let message = match std::fs::remove_file(path) {
Ok(()) => "ok",
Err(e) if e.kind() == ErrorKind::NotFound => "not found",
Err(e) if e.kind() == ErrorKind::IsADirectory => "is a directory",
Err(e) => "other",
}
println!("{message}");
}
Related
I am working on my first Rust project, a CLI application to find large files on a local filesystem. Rust has excellent documentation regarding match statements but I do not see how I can assign a value returned by the function passed to the match statement in a new variable:
pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
let mut results_map: Option<Results>;
match search(&config.root_path) {
Err(e) => eprintln!("Error when calling run(): {:?}", e),
Ok(results) => results_map = results), // how to properly assign value returned by function here?
}
Ok(())
}
pub struct Results {
result_map: HashMap<String, u64>,
}
pub fn search(path: &str) -> Result<Results, io::Error> {
let root_path = Path::new(path);
let mut results = Results { result_map: HashMap::<String, u64>::new()};
match visit_dirs(root_path, &mut results) {
Err(e) => eprintln!("Error calling visit_dirs() from search(): {:?}", e),
_ => (),
}
Ok(results)
}
The functions you're calling in the match statements, search and visit_dirs return Result types. You have to use one of the arms of the match statement to cover the Ok (happy) case, next to where you're already covering the Err (error) case:
match search(&config.root_path) {
Ok(results) => return results,
Err(e) => eprintln!("Error when calling run(): {:?}", e),
}
Or you can assign the result of the match expression to a new variable:
let new_variable = match search(&config.root_path) {
Ok(results) => results, // no return here
Err(e) => {
eprintln!("Error when calling run(): {:?}", e)
// Either choose to panic (not recommended)
// panic!("Bad times lie ahead")
// Or propagate the error from your own Result
return Err(e);
},
}
You may want to look into the ? operator to simplify this in the common case.
I'm having some problems with some basic I/O stuff. Specifically, the text "Please enter your name" is written to the output after I type in my name and hit Enter:
use std::io;
fn main() {
print!("Please enter your name: ");
let mut name = String::new();
match io::stdin().read_line(&mut name) {
Ok(_) => println!(""),
Err(err) => println!("Could not parse input: {}", err)
}
println!("Hello, {}!", name.trim());
}
gives the following output:
Compiling chat v0.1.0 (file:///home/marcus/dev/rust/chat)
Running `target/debug/chat`
marcus
Please enter your name:
Hello, marcus!
Where the first "marcus" was entered by me. Why won't the program print "Please enter your name" before waiting for input?
Is it possible to "do nothing" if a returned Result is Ok? In the example, Ok() means that I have saved the input in the variable name. That's great. But what do I do with Ok() => in this case?
Why won't the program print "Please enter your name" before waiting for input?
Well, it did. It's just that, for performance reasons, standard output is buffered. The write completed, but it was only writing to memory. If you want it to actually display to the user, you have to trigger a flush. This can be done either by writing a newline, or by doing it explicitly:
io::Write::flush(&mut io::stdout()).expect("flush failed!");
// If you "use" `io::Write`...
io::stdout().flush().expect("flush failed!");
Also, is it possible to "do nothing" if a returned Result is Ok?
Sure. Just... do nothing.
match io::stdin().read_line(&mut name) {
Ok(_) => { /* nothing */ },
Err(err) => println!("Could not parse input: {}", err)
}
The relevant requirement here is that all arms in a match have to have the same result type. In the case of println!, it results in a (); aside from an empty block (or another function that returns ()), you can just use a literal:
match io::stdin().read_line(&mut name) {
Ok(_) => (),
Err(err) => println!("Could not parse input: {}", err)
}
This is explained on the documentation for print!. Since print! does not emit a newline and stdout is line-buffered, you won't see any output. You can manually flush stdout:
use std::io::{self, Write};
print!("Please enter your name: ");
io::stdout().flush();
For your second question you can always return unit explicitly:
Ok(_) => (),
So your program becomes:
use std::io::{self, Write};
fn main() {
print!("Please enter your name: ");
io::stdout().flush();
let mut name = String::new();
match io::stdin().read_line(&mut name) {
Ok(_) => (),
Err(err) => println!("Could not parse input: {}", err)
}
println!("Hello, {}!", name.trim());
}
As #Veedrac pointed out in their (now deleted) comment, you can use an if let expression in place of the match on the result of read_line:
if let Err(err) = io::stdin().read_line(&mut name) {
println!("Could not parse input: {}", err)
}
use std::io::{self, Write};
fn main() {
print!("Please enter your name: ");
let _ = io::stdout().flush();
// your io code
}
I'm writing a function that will be called in an infinite loop and only execute something when getting well-formed data from a web-service. If the service is down, returns non-json, or returns json we do not understand, the function should just log the error and return (to be called again after a pause).
I found myself copying and pasting something like this:
let v = match v {
Ok(data) => data,
Err(error) => {
println!("Error decoding json: {:?}", error);
return;
}
};
The body of the error matcher would be different each time. Sometimes it's panic, sometimes it has different messages, and sometimes elements of error could be broken down further to form a better message, but the rest of the construct would be the same.
Is there a shorthand for this? I'm aware of the ? syntax, but that's for propagation. I don't feel that propagation will help with the scenario when you need slightly different processing in case of the error like in the scenario described above. This is because the particular differences in handling belong right here, not up the stack.
I have not written a lot of code in Rust yet so it is very likely that I'm missing something obvious.
In C#, the above would look something like this:
if (v == null)
{
Console.WriteLine("Error decoding json!");
return;
}
or
if (error != null)
{
Console.WriteLine($"Error decoding json: {error}");
return;
}
both of which is much less verbose than in Rust.
If I understood the comments below, one way of shortening would be something like this:
if let Err(error) = v {
println!("Error decoding json: {:?}", error);
return;
}
let v = v.unwrap();
This looks more compact, thank you. Is this idiomatic? Would you write it this way?
I don't feel that propagation will help with the scenario when you need slightly different processing in case of the error like in the scenario described above. This is because the particular differences in handling belong right here, not up the stack.
This is something a custom error type can help with. In this case you have a common behavior ("log an error") and you want to do that in slightly different ways for different values. It makes sense to move the "log an error" part up to the caller (let's call the function try_poll):
loop {
if let Err(e) = try_poll() {
println!("{}", e);
}
sleep(100);
}
And create a type that implements Display, and From<E> for each error type E:
enum PollError {
NetworkError(NetworkError),
JsonParseError(JsonParseError),
}
impl fmt::Display for PollError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
PollError::NetworkError(ref e) => write!(f, "Error downloading file: {:?}", e),
PollError::JsonParseError(ref e) => write!(f, "Error parsing JSON: {:?}", e),
}
}
}
impl From<NetworkError> for PollError {
fn from(e: NetworkError) -> Self {
PollError::NetworkError(e)
}
}
impl From<JsonParseError> for PollError {
fn from(e: JsonParseError) -> Self {
PollError::JsonParseError(e)
}
}
Now you can use ? to propagate the error, but the caller still doesn't have to be concerned with which error specifically it is.
fn try_poll() -> Result<(), PollError> {
let data = try_fetch_content()?;
let json = try_parse_json(data)?;
println!("Parsed {:?}", json);
Ok(())
}
(playground)
Ok, I want that, but without all the From implementations.
The tedious part about this is all the impl Froms, which are necessary because of the custom error type. If the only thing that will ever be done with an error is log and ignore it, a custom error type is not particularly useful -- the only thing that really needs to be returned is the error message itself.
In that case, have try_poll instead return Result<(), String>, and use Result::map_err to turn each individual error immediately into an error message, before using ? to propagate it:
fn try_poll() -> Result<(), String> {
let data = try_fetch_content()
.map_err(|e| format!("Error downloading file: {:?}", e))?;
let json = try_parse_json(data)
.map_err(|e| format!("Error parsing JSON: {:?}", e))?;
println!("Parsed {:?}", json);
Ok(())
}
(playground)
The first edition of The Rust Programming Language has this to say about String as an error type:
A rule of thumb is to define your own error type, but a String error type will do in a pinch, particularly if you're writing an application. If you're writing a library, defining your own error type should be strongly preferred so that you don't remove choices from the caller unnecessarily.
As an alternative to a custom macro_rule you could also use ? with Option<T> and a trait extension for Result to print errors and convert successful values.
Playground
pub trait ResultOkPrintErrExt<T> {
fn ok_or_print_err(self, msg: &str) -> Option<T>;
}
impl<T, E> ResultOkPrintErrExt<T> for Result<T, E>
where
E: ::std::fmt::Debug,
{
fn ok_or_print_err(self, msg: &str) -> Option<T> {
match self {
Ok(v) => Some(v),
Err(e) => {
eprintln!("{}: {:?}", msg, e);
None
}
}
}
}
fn read_input() -> Result<u32, ()> {
// Ok(5)
Err(())
}
fn run() -> Option<()> {
let v: u32 = read_input().ok_or_print_err("invalid input")?;
println!("got input: {}", v);
Some(())
}
fn main() {
run();
}
I'm trying to use the Iron framework to build a simple backend in Rust. This handler is just supposed to return the content of a certain file and I can get it to work properly with unwrap() but I want to try to do proper error handling. This is how I would imagine it would look like:
fn get_content(res: &mut Request) -> IronResult<Response> {
let mut id = String::new();
res.body.read_to_string(&mut id).unwrap();
let file_path_string = &("../content/".to_string() + &id + ".rdt");
// TODO: Error handling
match File::open(file_path_string) {
Ok(f) => {
let mut s = String::new();
f.read_to_string(&mut s);
Ok(Response::with(((status::Ok), s)))
}
Err(err) => Err(Response::with(((status::InternalServerError), "File not found")))
};
}
This throws the error not all control paths return a value [E0269], which is fine. But if I add a response after the match part:
match File::open(file_path_string) {
Ok(f) => {
let mut s = String::new();
f.read_to_string(&mut s);
Ok(Response::with(((status::Ok), s)))
}
Err(err) => Err(Response::with(((status::InternalServerError), "File not found")))
};
Err(Response::with(((status::InternalServerError), "File not found")))
I instead get the error message:
expected `iron::error::IronError`,
found `iron::response::Response`
(expected struct `iron::error::IronError`,
found struct `iron::response::Response`) [E0308]
src/main.rs:95
Err(Response::with(((status::InternalServerError), "File not found")))
I think the problem is the collision between Rust Err and Iron Err? I'm not sure though. And I have not done much web development (or Rust for that matter) in the past so any feedback on the code is also appreciated!
UPDATE: I think this is more "The Rust Way" to do it? But I'm not sure
fn get_content(res: &mut Request) -> IronResult<Response> {
let mut id = String::new();
res.body.read_to_string(&mut id).unwrap();
let file_path_string = &("../content/".to_string() + &id + ".rdt");
// TODO: Error handling
let f;
match File::open(file_path_string) {
Ok(file) => f = file,
Err(err) => Err(HttpError::Io(err))
};
let mut s = String::new();
f.read_to_string(&mut s);
Ok(Response::with(((status::Ok), s)))
}
Having the code inside the error handling seems weird as read_to_string also needs to be taken care of and that would create a nested mess of error handling? However, these matching arms are obviously of incompatible types so it won't work... any suggestions?
An Ok() takes an Response, but an Err() takes an IronError.
Hence your call Err(...) is not valid when ... is a Response!
How to correct it? Well the first step is, you must create an IronError to send back. I believe (not familiar with Iron) that Iron will automatically an appropriate error code and that it's not your job to do that. In the documentation we find one key type implementing IronError:
pub enum HttpError {
Method,
Uri(ParseError),
Version,
Header,
TooLarge,
Status,
Io(Error),
Ssl(Box<Error + 'static + Send + Sync>),
Http2(HttpError),
Utf8(Utf8Error),
// some variants omitted
}
I can't see one which allows for an arbitrary string like "file not found". However, your use case is one of an IO failure, right? So it would make sense to use HttpError::Io with the std::IoError that you got back from File::open():
match File::open(file_path_string) {
Ok(f) => {
let mut s = String::new();
f.read_to_string(&mut s);
Ok(Response::with(((status::Ok), s)))
}
Err(err) => Err(HttpError::Io(err))
};
By the way, it also fixes your "TODO: error handling"! How beautiful!
(Code untested, please feel free to edit if compilation fails)
I'm having some problems with some basic I/O stuff. Specifically, the text "Please enter your name" is written to the output after I type in my name and hit Enter:
use std::io;
fn main() {
print!("Please enter your name: ");
let mut name = String::new();
match io::stdin().read_line(&mut name) {
Ok(_) => println!(""),
Err(err) => println!("Could not parse input: {}", err)
}
println!("Hello, {}!", name.trim());
}
gives the following output:
Compiling chat v0.1.0 (file:///home/marcus/dev/rust/chat)
Running `target/debug/chat`
marcus
Please enter your name:
Hello, marcus!
Where the first "marcus" was entered by me. Why won't the program print "Please enter your name" before waiting for input?
Is it possible to "do nothing" if a returned Result is Ok? In the example, Ok() means that I have saved the input in the variable name. That's great. But what do I do with Ok() => in this case?
Why won't the program print "Please enter your name" before waiting for input?
Well, it did. It's just that, for performance reasons, standard output is buffered. The write completed, but it was only writing to memory. If you want it to actually display to the user, you have to trigger a flush. This can be done either by writing a newline, or by doing it explicitly:
io::Write::flush(&mut io::stdout()).expect("flush failed!");
// If you "use" `io::Write`...
io::stdout().flush().expect("flush failed!");
Also, is it possible to "do nothing" if a returned Result is Ok?
Sure. Just... do nothing.
match io::stdin().read_line(&mut name) {
Ok(_) => { /* nothing */ },
Err(err) => println!("Could not parse input: {}", err)
}
The relevant requirement here is that all arms in a match have to have the same result type. In the case of println!, it results in a (); aside from an empty block (or another function that returns ()), you can just use a literal:
match io::stdin().read_line(&mut name) {
Ok(_) => (),
Err(err) => println!("Could not parse input: {}", err)
}
This is explained on the documentation for print!. Since print! does not emit a newline and stdout is line-buffered, you won't see any output. You can manually flush stdout:
use std::io::{self, Write};
print!("Please enter your name: ");
io::stdout().flush();
For your second question you can always return unit explicitly:
Ok(_) => (),
So your program becomes:
use std::io::{self, Write};
fn main() {
print!("Please enter your name: ");
io::stdout().flush();
let mut name = String::new();
match io::stdin().read_line(&mut name) {
Ok(_) => (),
Err(err) => println!("Could not parse input: {}", err)
}
println!("Hello, {}!", name.trim());
}
As #Veedrac pointed out in their (now deleted) comment, you can use an if let expression in place of the match on the result of read_line:
if let Err(err) = io::stdin().read_line(&mut name) {
println!("Could not parse input: {}", err)
}
use std::io::{self, Write};
fn main() {
print!("Please enter your name: ");
let _ = io::stdout().flush();
// your io code
}