I know that when handling errors using Result and Option, it can be expressed more concisely by using unwrap_or_else, unwrap_or_default, etc. instead of match.
The following is an example of expressing the match expression more concisely using unwrap.
let engine_name = match config.engine_name {
Some(name) => name,
None => host_name.clone(),
};
->
let engine_name = config.engine_name
.unwrap_or_else(|| host_name.clone());
let group_name = match config.group_name {
Some(name) => name,
None => String::from("")
};
->
let group_name = config.group_name.unwrap_or_default();
Questions
Is there a function I can use instead of match if I want to put a return statement instead of putting a different value when an error occurs?
let accept_service = ServiceBuilder::new().service(match AcceptService::new() {
Ok(service) => service,
Err(e) => return Err(format!("failed to bind server socket: {}", e).into()),
});
You can use a combination of map_err() and the error propagation operator ?:
ServiceBuilder::new()
.service(AcceptService::new().map_err(|e| format!("failed to bind server socket: {}", e)?));
Related
I'm trying to print out the error it throws when bmp::open failed
let image_result = bmp::open(argument);
let image = match image_result {
Ok(i) => i,
Err(error) => {
println!("Error! {error:?}");
},
};
However, I've been getting the below error message
Thanks in advance!
This is because in Rust, the match statement has to return the same type from its match arms. So your first match arm
Ok(i) => i
returns a type of Image where as second match arm
Err(error) => {
println!("Error! {error:?}");
}
does not return anything hence the compiler infer the return type as ()(unit) type.
There are multiple ways you could solve this, but it's really depends upon how you want to handle with the error case. If your intention is to handle only Ok case, you could destructure the Result.
if let Ok(i) = bmp::open(argument) {
print("Do something with {i}")
}
Alternatively you can panic if the file is failed to open.
let image_result = bmp::open(argument);
let image = match image_result {
Ok(i) => i,
Err(error) => {
panic!("Error! {error:?}");
}
};
OR with unwrap_or_else,
let img = bmp::open("test/rgbw.bmp").unwrap_or_else(|e| {
panic!("Failed to open: {}", e);
});
I currently have the below code to handle the case where I want to handle an error without propagation or continue the function. The use case for this is a web server controller, where I would prefer to manual handle possible errors - which is why the return type is HttpResponse.
I want to know if there is a way to do this without this unwrap call as in my understanding the compiler should know there is a way to unwrap to a value at this point with no risk of panic.
// ...
let result: Result<u8, Error> = func_that_could_error();
if result.is_err() {
return HttpResponse::InternalServerError();
}
let value: u8 = result.unwrap();
// ...
If you can, use ?:
let value = func_that_could_error()?;
Optionally mapping it to your error as #Jmb suggests:
let value = func_that_could_error()
.map_err(|_| HttpResponse::InternalServerError())?;
Otherwise, use let else:
let Ok(value) = func_that_could_error() else {
return HttpResponse::InternalServerError();
};
Or, for Rust < 1.65, match:
let value = match func_that_could_error() {
Err(_) => return HttpResponse::InternalServerError(),
Ok(v) => v,
};
I am confused about the Some(T) keyword.
I want to check for two variables, if the value is defined (not None). If that is the case, the value of this variables is processed.
I know the match pattern which works like this:
match value {
Some(val) => println!("{}", val),
None => return false,
}
If I use this pattern, it will get very messy:
match param {
Some(par) => {
match value {
Some(val) => {
//process
},
None => return false,
}
},
None => return false,
}
This can't be the right solution.
The is a possibility, to ask if the param and value is_some() That would effect code like that:
if param.is_some() && value.is_some() {
//process
}
But if I do it like that, I always have to unwrap param and value to access the values.
I thought about something like this to avoid that. But this code does not work:
if param == Some(par) && value == Some(val) {
//process
}
The idea is that the values are accessible by par and val like they are in the match version.
Is there any solution to do something like this?
If I have several Option values to match, I match on a tuple of the values:
enum Color {
Red,
Blue,
Green,
}
fn foo(a: Option<Color>, b: Option<i32>) {
match (a, b) {
(Some(Color::Blue), Some(n)) if n > 10 => println!("Blue large number"),
(Some(Color::Red), _) => println!("Red number"),
_ => (),
}
}
fn main() {
foo(Some(Color::Blue), None);
foo(Some(Color::Blue), Some(20));
}
This allows me to match the combinations that are interesting, and discard the rest (or return false, if that is what you want to do).
If your function is processing multiple Option values, and would like to discard them if they're not Some, your function could return an Option itself:
fn foo(param: Option<usize>, value: Option<usize>) -> Option<usize> {
let result = param? + value?;
Some(result)
}
This will short-circuit the function in case there's a None value stored in either param or value.
Please read the book for more information on the ? operator.
If your function can't return an Option, you can still get away with destructuring using if let or match:
let x = if let (Some(p), Some(v)) = (param, value) {
p + v
} else {
return 0;
}
let x = match (param, value) {
(Some(p), Some(v)) => p + v,
(Some(p), _) => p,
(_, Some(v) => v,
_ => return 0,
}
Please read What is this question mark operator about? for more information on the ? operator
Please read this chapter in Rust by Example for more information on destructuring multiple things at once
There's a couple more alternatives not yet listed:
If you're willing to use experimental features (and hence the nightly compiler) you can use a try block as an alternative of extracting a function.
#![feature(try_blocks)]
fn main() {
let par: Option<f32> = Some(1.0f32);
let value: Option<f32> = Some(2.0f32);
let x: Option<f32> = try { par? + value? };
println!("{:?}", x);
}
Another alternative is to use map which only applies if the value is not None
let x: Option<f32> = par.map(|p| value.map(|v| p + v));
If the environment variable SOMEVALUE is found, it should be bound to k:
let k = match env::var(SOMEVALUE) {
Ok(val) => {return val},
Err(e) => println!("could not find {}: {}", SOMEVALUE, e),
};
Compiling this code gives the following error
|
11 | Ok(val) => {return val},
| ^^^ expected (), found struct `std::string::String`
|
= note: expected type `()`
found type `std::string::String`
What Victor wrote is correct, but you probably want to actually bind a value to k; in that case you should change your code to:
let k = match env::var(SOMEVALUE) {
Ok(val) => val,
Err(e) => panic!("could not find {}: {}", SOMEVALUE, e),
};
Equivalently, you can use Result::unwrap_or_else:
let k = env::var(SOMEVALUE).unwrap_or_else(|e| {
panic!("could not find {}: {}", SOMEVALUE, e)
});
In Rust, everything is an expression, there are only a few statements. Each expression returns a value and if the expression does not return anything explicitly, it returns a () (unit) value implicitly.
Your match expression returns different types from 2 branches - a std::String from Ok branch and () from Err branch.
To fix this, you must return something from the second branch too:
let k = match env::var(SOMEVALUE) {
Ok(val) => {return val},
Err(e) => {
println!("could not find {}: {}", SOMEVALUE, e);
String::default()
},
};
This returns a value of the same type - std::String.
Another option you can do is following (This just shows how we can use env::var_os to achieve same purpose)
let spdk_dir = match env::var_os("SPDK_DIR") {
Some(val) => val.into_string().unwrap(),
None => panic!("SPDK_DIR is not defined in the environment")
};
Here, we try to read the environment variable SPDK_DIR and if it is not defined, we exit the program.
How can I check a variable (entered by user) is a number such as an int, float, or something else?
I want to do this with a match expression:
let mut input = String::new();
io::stdin().read_line(&mut input);
let result = match input {
// !!??
}
is it possible by match?
If you want to match against something then you need something to destructure on. You can match against string slices, but only a finite set, so that doesn't help here.
So let's have an enum to match on:
enum Value {
Int(isize),
Float(f64),
}
use Value::*; // Make the variants available without Value::
Then you need to parse the string into the appropriate type. You can use the parse method for each type (if you're happy with Rust's syntax for those; otherwise you might need a fancier parser):
fn parse_string(s: &str) -> Option<Value> {
if let Ok(i) = s.parse() { // inferred as isize from next line
Some(Int(i))
} else if let Ok(f) = s.parse() {
Some(Float(f))
} else {
None
}
}
Note that when trying parses in sequence like this the order matters; "123" would parse as an f64 too.
I'm turning any parse errors into None and using Option rather than Result because it's not clear what the error would be (since each parse can return its own); in a real application I might have a ParseError type.
Now we can match on the result:
fn main() {
let x = "123";
match parse_string(x) {
Some(Int(i)) => println!("int {}", i),
Some(Float(f)) => println!("float {}", f),
None => println!("Didn't parse"),
}
let x = "123.5";
match parse_string(x) {
Some(Int(i)) => println!("int {}", i),
Some(Float(f)) => println!("float {}", f),
None => println!("Didn't parse"),
}
}
Runnable playground link