What is the idiomatic way to handle/unwrap nested Result types? - coding-style

I read that using unwrap on a Result is not a good practice in Rust and that it's better to use pattern matching so any error that occurred can be handled appropriately.
I get the point, but consider this snippet that reads a directory and prints the accessed time for each entry:
use std::fs;
use std::path::Path;
fn main() {
let path = Path::new(".");
match fs::read_dir(&path) {
Ok(entries) => {
for entry in entries {
match entry {
Ok(ent) => {
match ent.metadata() {
Ok(meta) => {
match meta.accessed() {
Ok(time) => {
println!("{:?}", time);
},
Err(_) => panic!("will be handled")
}
},
Err(_) => panic!("will be handled")
}
},
Err(_) => panic!("will be handled")
}
}
},
Err(_) => panic!("will be handled")
}
}
I want to handle every possible error in the code above (the panic macro is just a placeholder). While the code above works, I think it's ugly. What is the idiomatic way to handle a case like this?

I read that using unwrap on a Result is not a good practice in Rust.
It's not that easy. For example, read my answer here to learn a bit more. Now to your main problem:
Reduce right shift by passing Ok value to the outside
One big issue with your code is the right shift: for example, the meta.accessed() call is indented a whole lot. We can avoid this by passing the value we want to work with out of the match:
let entries = match fs::read_dir(&path) {
Ok(entries) => entries, // "return" from match
Err(_) => panic!("will be handled"),
};
for entry in entries { // no indentation! :)
// ...
}
That's already a very good way to make the code more readable.
Using the ? operator to pass the error to the calling function
Your function could return a Result<_, _> type in order to pass the error to the calling function (yes, even main() can return Result). In this case you can use the ? operator:
use std::{fs, io};
fn main() -> io::Result<()> {
for entry in fs::read_dir(".")? {
println!("{:?}", entry?.metadata()?.accessed()?);
}
Ok(())
}
Use helper methods of Result
There are also many helper methods, like map() or and_then(), for the Result type. and_then is helpful if you want to do something, if the result is Ok and this something will return a result of the same type. Here is your code with and_then() and manual handling of the error:
fn main() {
let path = Path::new(".");
let result = fs::read_dir(&path).and_then(|entries| {
for entry in entries {
let time = entry?.metadata()?.accessed()?;
println!("{:?}", time);
}
Ok(())
});
if let Err(e) = result {
panic!("will be handled");
}
}
There really isn't only one way to do this kind of error handling. You have to get to know all the tools you can use and then need to choose the best for your situation. However, in most situations, the ? operator is the right tool.

Result happens to have a lot of convenience methods for these kinds of things:
use std::fs;
use std::path::Path;
fn main() {
let path = Path::new(".");
match fs::read_dir(&path) {
Ok(entries) => {
for entry in entries {
match entry.and_then(|e| e.metadata()).map(|m| m.accessed()) {
Ok(time) => {
println!("{:?}", time);
},
Err(_) => panic!("will be handled")
}
}
},
Err(_) => panic!("will be handled")
}
}
And usually you will not have so much logic in main and will simply be able to use ? or try! in another function:
use std::fs;
use std::path::Path;
fn print_filetimes(path: &Path) -> Result<(), std::io::Error> {
for entry in fs::read_dir(&path)? {
let time = entry.and_then(|e| e.metadata()).map(|m| m.accessed())?;
println!("{:?}", time);
}
Ok(())
}
fn main() {
let path = Path::new(".");
match print_filetimes(path) {
Ok(()) => (),
Err(_) => panic!("will be handled"),
}
}

Related

Prae:Wrapper: need to use iter_mut of interior Vec but Prae:Wrapper only provides immutable access

I'm using the prae crate for validation and the following function gives me errors:
fn advance_rotors(&mut self) {
self.rotors.get()[0].rotate();
let mut iterhandle = self.rotors.iter_mut().peekable(); // Error at iter_mut() #0599
while let Some(el) = iterhandle.next() {
match iterhandle.peek_mut() {
Some(next_rotor) => match el.should_advance_next() {
true => {
next_rotor.rotate(); // This line requires mutable access to next_rotor
}
false => (),
},
None => (),
}
}
}
and the definition of my struct here:
pub struct Enigma {
reflector: Reflector,
rotors: RotorConfig, // Only mutable via getter and setter functions
}
the struct of interest here is RotorConfig which is generated using the define! macro from prae. Here's the code:
prae::define! {
#[derive(Debug)]
RotorConfig: Vec<Rotor>; // I need to be able to call the rotate method of each rotor in this vec. This requires mutability
validate(RotorConfigError) |config| {
match config.len(){
3..=4 => (),
_ => return Err(RotorConfigError::Size)
}
match config.iter().unique().count(){
3..=4 =>(),
_ => return Err(RotorConfigError::Duplicate)
}
Ok(())
};
}
the issue stems from the fact that prae only allows for immutable access to the internal representation via getter and setter functions so as to ensure the validity of the values inside. As you can see in my advance_rotors function I wrote before implementing validation I'm getting an error because I need to call rotor.rotate mutably. I'm at a loss as to how to accomplish this
After posting this I realized that I can simply provide interior mutability by using the following impl block
impl RotorConfig{
fn advance_rotors(&mut self)
{
self.0[0].rotate();
let mut iterhandle = self.0.iter_mut().peekable();
while let Some(el) = iterhandle.next() {
match iterhandle.peek_mut() {
Some(next_rotor) => match el.should_advance_next() {
true => {
next_rotor.rotate();
}
false => (),
},
None => (),
}
}
}
}
As you can see the function largely remains unchanged except that we replace self.rotors with self.0

Cast a Rust enum to a sub-enum

I'm creating subsets of std::sync::atomic::Ordering:
use std::sync::atomic::Ordering;
pub enum StoreOrdering {
Relaxed,
Release,
SeqCst
}
impl Into<Ordering> for StoreOrdering {
fn into(self) -> Ordering {
match self {
Self::Relaxed => Ordering::Relaxed,
Self::Release => Ordering::Release,
Self::SeqCst => Ordering::SeqCst
}
}
}
impl std::convert::TryFrom<Ordering> for StoreOrdering {
type Error = (); // HACK
fn try_from(ord: Ordering) -> Result<Self, Self::Error> {
match ord {
Ordering::Relaxed => Ok(Self::Relaxed),
Ordering::Release => Ok(Self::Release),
Ordering::SeqCst => Ok(Self::SeqCst),
_ => Err(())
}
}
}
enum LoadOrdering {
Acquire,
Relaxed,
SeqCst
}
// ???
As you can see, now I need to write those two impls with matches again for StoreOrdering <-> LoadOrdering and maybe even for StoreOrdering <-> LoadOrdering - as well as for any enum subset. How to avoid such boilerplate?
Rust doesn't support duck typing like C++ does with templates. The only functionality that generics can access is determined by the trait bounds.
So any duck-type-like behaviour must be done with macros.
For this, you could use the given macro below.
It only works for simple C-style macros. It creates the enum and auto-generates the conversions to the given super-enum.
use std::sync::atomic::Ordering;
use std::convert::TryInto;
// Create the store ordering
sub_enum!(StoreOrdering of Ordering {
Relaxed,
Release,
SeqCst
});
// Create the load ordering
sub_enum!(LoadOrdering of Ordering {
Acquire,
Relaxed,
SeqCst
});
#[macro_export]
macro_rules! sub_enum {
($sub_enum_name:ident of $super_enum_name:ty {
$($variant:ident),* $(,)?
}) => {
pub enum $sub_enum_name {
$($variant,)*
}
impl From<$sub_enum_name> for $super_enum_name {
fn from(val: $sub_enum_name) -> $super_enum_name {
match val {
$(<$sub_enum_name>::$variant => <$super_enum_name>::$variant,)*
}
}
}
impl std::convert::TryFrom<$super_enum_name> for $sub_enum_name {
type Error = ();
fn try_from(val: $super_enum_name) -> Result<Self, Self::Error> {
match val {
$(<$super_enum_name>::$variant => Ok(Self::$variant),)*
_ => Err(())
}
}
}
}
}
fn main() {
let store = StoreOrdering::SeqCst;
let general: Ordering = store.into();
let load: LoadOrdering = general.try_into().unwrap();
}
Playground link
A lot could be improved still, of course.
However, this should do for your problem right now.

Mutating self in enum method

This is cobbled together to illustrate the problem that I have with the switch function. I do not have problem printing "Left" "Right" endlessly.
The point of switch is to swap the value of enum to another. This solution doesn't work because presumably the switch moves t into itself so it's no longer usable. Use of mutable references causes a whole host of other problems, like with lifetime and mismatched types. The documentation had instructions how to do this with structs, but not enums. The compiler suggested implementing Copy and Clone to the enum, but that did nothing useful.
How is this type of method supposed to be made in Rust?
fn main() {
let mut t = Dir::Left;
loop {
match &t {
&Dir::Left => println!("Left"),
&Dir::Right => println!("Right"),
}
t.switch();
}
}
enum Dir {
Left,
Right,
}
impl Dir {
//this function is the problem here
fn switch(mut self) {
match self {
Dir::Left => self = Dir::Right,
Dir::Right => self = Dir::Left,
};
}
}
Of course I could just make it so
t = t.switch();
and
fn switch(mut self) -> Self {
match self {
Dir::Left => return Dir::Right,
Dir::Right => return Dir::Left,
};
}
But I feel that would be comparatively clumsy solution, and I would like to avoid it if at all possible.
Your method consumes your data instead of borrowing it. If you borrow it, it works fine:
impl Dir {
fn switch(&mut self) {
*self = match *self {
Dir::Left => Dir::Right,
Dir::Right => Dir::Left,
};
}
}

How to compare enum without pattern matching

I want to apply filter on an iterator and I came up with this one and it works, but it's super verbose:
.filter(|ref my_struct| match my_struct.my_enum { Unknown => false, _ => true })
I would rather write something like this:
.filter(|ref my_struct| my_struct.my_enum != Unknown)
This gives me a compile error
binary operation `!=` cannot be applied to type `MyEnum`
Is there an alternative to the verbose pattern matching? I looked for a macro but couldn't find a suitable one.
Use matches!, e.g.:
matches!(my_struct.my_enum, Unknown)
Alternatively, you can use PartialEq trait, for example, by #[derive]:
#[derive(PartialEq)]
enum MyEnum { ... }
Then your "ideal" variant will work as is. However, this requires that MyEnum's contents also implement PartialEq, which is not always possible/wanted.
I'd use pattern matching, but I'd move it to a method on the enum so that the filter closure is tidier:
#[derive(Debug)]
enum Thing {
One(i32),
Two(String),
Unknown,
}
impl Thing {
fn is_unknown(&self) -> bool {
match *self {
Thing::Unknown => true,
_ => false,
}
}
}
fn main() {
let things = [Thing::One(42), Thing::Two("hello".into()), Thing::Unknown];
for t in things.iter().filter(|s| !s.is_unknown()) {
println!("{:?}", t);
}
}
You can combine this with the matches macro as well:
fn is_unknown(&self) -> bool {
matches!(self, Thing::Unknown)
}
See also:
Compare enums only by variant, not value
You can use if let Some(x) = option { then } idiom with the same if let construct but without destructuring:
if let Unknown = my_struct.my_enum { false } else { true }

Return an inline-defined enum from a function?

I'm diving into rust, and I'm trying to do something like this:
match send("select * from User;") {
ConnError => println!("Connection error!"),
DBError(e) => println!("Database error {}", e),
Ok(response) => {
...
}
}
and I'm trying to figure out a compact way of defining the send function. I saw the Result enum, but it only handles one kind of error at a time. I was hoping that I could define my own enum like this:
fn send(query: str) -> enum { Ok(Box<Response>), ConnError, DBError(str) } {
...
}
alas, it is not possible, it's complaining about the unexpected 'enum' keyword. Is there any way to do what I'm trying here, or perhaps make Result handle multiple error types? Thanks!
As you say, you can use Result but you have to define the enum with your error types separately, as you can't define it directly in the return of your function.
Something like this:
use std::rand::distributions::{IndependentSample, Range};
fn main() {
match send("select * from foo") {
Ok(Response) => println!("response"),
Err(e) => match e {
ConnError => println!("connection error"),
DbError(err) => println!("{}", err)
}
}
}
// the enum with your errors
enum DataLayerError {
ConnError,
DbError(String)
}
struct Response; /*...*/
fn send(_query: &str) -> Result<Response, DataLayerError> {
let between = Range::new(0u, 2);
let mut rng = std::rand::task_rng();
// return a random result
match between.ind_sample(&mut rng) {
0 => Ok(Response),
1 => Err(DbError("yikes".to_string())),
2 => Err(ConnError),
_ => unreachable!()
}
}

Resources