Return an inline-defined enum from a function? - enums

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!()
}
}

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.

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

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"),
}
}

Modifying a value inside an enum while matching

Is it possible to directly modify a value embedded inside an enum?
The following fails with error: cannot borrow immutable anonymous field `a.0` as mutable, even though I used ref mut.
enum Foo {
Bar(usize),
}
fn main() {
let a = Foo::Bar(10);
match a {
Foo::Bar(ref mut val) => *val = 33,
}
match a {
Foo::Bar(val) => println!("{}", val), // should print 33
}
}
That's not a huge problem because I can do the following as a work-around:
match a {
Foo::Bar(val) => a = Foo::Bar(33),
}
But is this the correct way?
You need to make the binding to a mutable.
enum Foo {
Bar(usize),
}
fn main() {
let mut a = Foo::Bar(10);
match a {
Foo::Bar(ref mut val) => *val = 33,
}
match a {
Foo::Bar(val) => println!("{}", val), // 33
}
}

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 }

Resources