I'm trying to simplify the following code:
fn foo(a: i32) -> Option<i32> {
match bar() {
None => None,
Some(strct) => match strct.field {
None => None,
Some(field) => Some(a * field),
}
}
}
This is where I've gotten so far. It works, but I can't figure out how to remove the unwrap() call to make it functionally identical to the original version.
fn foo(a: i32) -> Option<i32> {
bar().and_then(|strct| Some(a * strct.field.unwrap()))
}
P.S.: How would you recommend getting an intuition about doing this kind of stuff? I routinely run into these problems with Rust and it's always a struggle.
Here you should use map instead of unwrap.
fn foo(a: i32) -> Option<i32> {
bar().and_then(|s| s.field.map(|f| a * f))
}
As for developing an intuition, that will take time. Keep reading and writing rust code (and code in other functional languages) and this intuition will come.
Related
In Rust the main function is defined like this:
fn main() {
}
This function does not allow for a return value though. Why would a language not allow for a return value and is there a way to return something anyway? Would I be able to safely use the C exit(int) function, or will this cause leaks and whatnot?
As of Rust 1.26, main can return a Result:
use std::fs::File;
fn main() -> Result<(), std::io::Error> {
let f = File::open("bar.txt")?;
Ok(())
}
The returned error code in this case is 1 in case of an error. With File::open("bar.txt").expect("file not found"); instead, an error value of 101 is returned (at least on my machine).
Also, if you want to return a more generic error, use:
use std::error::Error;
...
fn main() -> Result<(), Box<dyn Error>> {
...
}
std::process::exit(code: i32) is the way to exit with a code.
Rust does it this way so that there is a consistent explicit interface for returning a value from a program, wherever it is set from. If main starts a series of tasks then any of these can set the return value, even if main has exited.
Rust does have a way to write a main function that returns a value, however it is normally abstracted within stdlib. See the documentation on writing an executable without stdlib for details.
As was noted by others, std::process::exit(code: i32) is the way to go here
More information about why is given in RFC 1011: Process Exit. Discussion about the RFC is in the pull request of the RFC.
The reddit thread on this has a "why" explanation:
Rust certainly could be designed to do this. It used to, in fact.
But because of the task model Rust uses, the fn main task could start a bunch of other tasks and then exit! But one of those other tasks may want to set the OS exit code after main has gone away.
Calling set_exit_status is explicit, easy, and doesn't require you to always put a 0 at the bottom of main when you otherwise don't care.
Try:
use std::process::ExitCode;
fn main() -> ExitCode {
ExitCode::from(2)
}
Take a look in doc
or:
use std::process::{ExitCode, Termination};
pub enum LinuxExitCode { E_OK, E_ERR(u8) }
impl Termination for LinuxExitCode {
fn report(self) -> ExitCode {
match self {
LinuxExitCode::E_OK => ExitCode::SUCCESS,
LinuxExitCode::E_ERR(v) => ExitCode::from(v)
}
}
}
fn main() -> LinuxExitCode {
LinuxExitCode::E_ERR(3)
}
You can set the return value with std::os::set_exit_status.
I'm trying to understand how to use the failure crate. It works splendidly as a unification of different types of standard errors, but when creating custom errors (Fails), I do not understand how to match for custom errors. For example:
use failure::{Fail, Error};
#[derive(Debug, Fail)]
pub enum Badness {
#[fail(display = "Ze badness")]
Level(String)
}
pub fn do_badly() -> Result<(), Error> {
Err(Badness::Level("much".to_owned()).into())
}
#[test]
pub fn get_badness() {
match do_badly() {
Err(Badness::Level(level)) => panic!("{:?} badness!", level),
_ => (),
};
}
fails with
error[E0308]: mismatched types
--> barsa-nagios-forwarder/src/main.rs:74:9
|
73 | match do_badly() {
| ---------- this match expression has type `failure::Error`
74 | Err(Badness::Level(level)) => panic!("{:?} badness!", level),
| ^^^^^^^^^^^^^^^^^^^^^ expected struct `failure::Error`, found enum `Badness`
|
= note: expected type `failure::Error`
found type `Badness`
How can I formulate a pattern which matches a specific custom error?
You need to downcast the Error
When you create a failure::Error from some type that implements the Fail trait (via from or into, as you do), you temporarily hide the information about the type you're wrapping from the compiler. It doesn't know that Error is a Badness - because it can also be any other Fail type, that's the point. You need to remind the compiler of this, the action is called downcasting. The failure::Error has three methods for this: downcast, downcast_ref and downcast_mut. After you've downcast it, you can pattern match on the result as normal - but you need to take into account the possibility that downcasting itself may fail (if you try to downcast to a wrong type).
Here's how it'd look with downcast:
pub fn get_badness() {
if let Err(wrapped_error) = do_badly() {
if let Ok(bad) = wrapped_error.downcast::<Badness>() {
panic!("{:?} badness!", bad);
}
}
}
(two if lets can be combined in this case).
This quickly gets very unpleasant if more than one error type needs to be tested, since downcast consumes the failure::Error it was called on (so you can't try another downcast on the same variable if the first one fails). I sadly couldn't figure out an elegant way to do this. Here's a variant one shouldn't really use (panic! in map is questionable, and doing anything else there would be plenty awkward, and I don't even want to think about more cases than two):
#[derive(Debug, Fail)]
pub enum JustSoSo {
#[fail(display = "meh")]
Average,
}
pub fn get_badness() {
if let Err(wrapped_error) = do_badly() {
let e = wrapped_error.downcast::<Badness>()
.map(|bad| panic!("{:?} badness!", bad))
.or_else(|original| original.downcast::<JustSoSo>());
if let Ok(so) = e {
println!("{}", so);
}
}
}
or_else chain should work OK if you actually want to produce some value of the same type from all of the possible\relevant errors. Consider also using non-consuming methods if a reference to the original error is fine for you, as this would allow you to just make a series of if let blocks , one for each downcast attempt.
An alternative
Don't put your errors into failure::Error, put them in a custom enum as variants. It's more boilerplate, but you get painless pattern matching, which the compiler also will be able to check for sanity. If you choose to do this, I'd recommend derive_more crate which is capable of deriving From for such enums; snafu looks very interesting as well, but I have yet to try it. In its most basic form this approach looks like this:
pub enum SomeError {
Bad(Badness),
NotTooBad(JustSoSo),
}
pub fn do_badly_alt() -> Result<(), SomeError> {
Err(SomeError::Bad(Badness::Level("much".to_owned())))
}
pub fn get_badness_alt() {
if let Err(wrapper) = do_badly_alt() {
match wrapper {
SomeError::Bad(bad) => panic!("{:?} badness!", bad),
SomeError::NotTooBad(so) => println!("{}", so),
}
}
}
I implemented a BoxedIterator in Rust that just boxes another Iterator as a trait object. The full implementation is on Github. Why does Rust compile this code without complaint but fail with an "out of memory" message (OOM) when it first tries to call next on the Iterator trait object in the Box?
As far as I can tell it doesn't allocate much memory before failing, so I'm inclined to think the OOM message is not correct.
//! BoxedIterator just wraps around a box of an iterator, it is an owned trait object.
//! This allows it to be used inside other data-structures, such as a `Result`.
//! That means that you can `.collect()` on an `I where I: Iterator<Result<V, E>>` and get out a
//! `Result<BoxedIterator<V>, E>`. And then you can `try!` it. At least, that was my use-case.
use std::iter::FromIterator;
use std::iter::IntoIterator;
pub struct BoxedIterator<T> {
iter: Box<Iterator<Item = T>>,
}
impl<T> Iterator for BoxedIterator<T> {
type Item = T;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.iter.next() // The OOM comes from this call of `next`
}
}
impl<T> FromIterator<T> for BoxedIterator<T> {
fn from_iter<I>(iter: I) -> Self
where I: IntoIterator<Item = T>,
I::IntoIter: 'static
{
BoxedIterator { iter: Box::new(iter.into_iter()) }
}
}
use std::fs::File;
use std::io;
fn main() {
let iter: Result<BoxedIterator<File>, io::Error> =
vec!["/usr/bin/vi"].iter().cloned().map(File::open).collect();
let mut iter = iter.unwrap();
println!("{:?}", iter.next());
}
I don't think I'm going to use this code, as I've figured that my use case will need to traverse the Iterator of Results completely to extract any errors so I might as well gather them in a Vec at that point. But I'm still curious about this OOM.
While creating a minimal example, I found that without doing the File IO, I get a segfault:
use iterator::BoxedIterator;
fn main() {
let iter: Result<BoxedIterator<&str>, ()> =
vec![Ok("test1"), Ok("test2")].iter().cloned().collect();
let mut iter = iter.unwrap();
println!("{:?}", iter.next());
}
If I don't use any Result, just create a BoxedIterator with collect, the code works as expected:
use iterator::BoxedIterator;
fn main() {
let mut iter: BoxedIterator<&str> = vec!["test1", "test2"].iter().cloned().collect();
println!("{:?}", iter.next());
// prints: Some("test1")
}
Your implementation of FromIterator isn't correct; specifically, you aren't allowed to put an I::IntoIter: 'static bound in that position. The bounds on your implementation have to match the bounds on the trait itself. The compiler should diagnose this, but currently doesn't.
At a higher level, I'm not sure what you're trying to do. Where do you expect the File handles to be stored? You would normally write something like this:
let files: Result<Vec<File>, io::Error> =
["/bin/bash"].iter().cloned().map(File::open).collect();
I'm writing a function that iterates over a vector of Result and returns success if they all were successful, or an error if any failed. Limitations in error::Error are frustrating me and I'm not sure how to work around them. Currently I have something like:
let mut errors = Vec::new();
for result in results {
match result {
Err(err) => errors.push(err),
Ok(success) => { ... }
}
}
if errors.is_empty() {
return Ok(())
else {
return Err(MyErrorType(errors))
}
The problem with my current approach is that I can only set one error to be the cause of MyErrorType, and my error's description needs to be a static String so I can't include the descriptions of each of the triggering failures. All of the failures are potentially relevant to the caller.
There is no convention that I know of, and indeed I have never had the issue of attempting to report multiple errors at once...
... that being said, there are two points that may help you:
There is no limitation that the description be a 'static String, you are likely confusing &'static str and &str. In fn description(&self) -> &str, the lifetime of str is linked to the lifetime of self (lifetime elision) and therefore an embedded String satisfies the constraints
Error is an interface to deal with errors uniformly. In this case, indeed, only a single cause was foreseen, however it does not preclude a more specific type to aggregate multiple causes and since Error allows downcasting (Error::is, Error::downcast, ...) the more specific type can be retrieved by the handler and queried in full
As such, I would suggest that you create a new concrete type solely dedicated to holding multiple errors (in a Vec<Box<Error>>), and implementing the Error interface. It's up to you to decide on the description and cause it will expose.
A single type will let your clients test more easily for downcasting than having an unknown (and potentially growing as time goes) number of potential downcast targets.
expanding a bit on point 1 of Matthieu's good answer.
The point where you're likely running into trouble (I know I did when I tried to implement Error) is that you want to have a dynamic description().
// my own error type
#[derive(Debug)] struct MyError { value: u8 }
impl fmt::Display for MyError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "bummer! Got a {}", self.value)
}
}
// I am now tempted to add the problematic value dynamically
// into the description, but I run into trouble with lifetimes
// this DOES NOT COMPILE because the String I'm building
// goes out of scope and I can't return a reference to it
impl error::Error for MyError {
fn description(&self) -> &str {
&format!("can't put a {} here!", self.value)
}
}
solution 1
Don't dynamically build description(). Just use a static str. This is what most implementations of Error on github seem to do.
If you need to retrieve and display (or log) the value you can always access it from your MyError type. Plus Display (that you must implement for all Error impls) does allow you to create dynamic strings.
I created a contrived example on the playground that shows how to track multiple errors.
solution 2
(what Matthieu is suggesting) you can store the error message in the error itself.
#[derive(Debug)] struct MyError { value: u8, msg: String }
impl MyError {
fn new(value: u8) -> MyError {
MyError { value: value, msg: format!("I don't like value {}", value) }
}
}
// now this works because the returned &str has the same lifetime
// as self
impl error::Error for MyError {
fn description(&self) -> &str {
&self.msg
}
}
I'm trying to involve std::error::FromError trait as widely as possible in my projects to take advantage of try! macro. However, I'm a little lost with these errors conversions between different mods.
For example, I have mod (or crate) a, which has some error handling using it's own Error type, and implements errors conversion for io::Error:
mod a {
use std::io;
use std::io::Write;
use std::error::FromError;
#[derive(Debug)]
pub struct Error(pub String);
impl FromError<io::Error> for Error {
fn from_error(err: io::Error) -> Error {
Error(format!("{}", err))
}
}
pub fn func() -> Result<(), Error> {
try!(writeln!(&mut io::stdout(), "Hello, world!"));
Ok(())
}
}
I also have mod b in the same situation, but it implements error conversion for num::ParseIntError:
mod b {
use std::str::FromStr;
use std::error::FromError;
use std::num::ParseIntError;
#[derive(Debug)]
pub struct Error(pub String);
impl FromError<ParseIntError> for Error {
fn from_error(err: ParseIntError) -> Error {
Error(format!("{}", err))
}
}
pub fn func() -> Result<usize, Error> {
Ok(try!(FromStr::from_str("14")))
}
}
Now I'm in my current mod super, which has it's own Error type, and my goal is to write a procedure like this:
#[derive(Debug)]
struct Error(String);
fn func() -> Result<(), Error> {
println!("a::func() -> {:?}", try!(a::func()));
println!("b::func() -> {:?}", try!(b::func()));
Ok(())
}
So I definitely need to implement conversions from a::Error and b::Error for my Error type:
impl FromError<a::Error> for Error {
fn from_error(a::Error(contents): a::Error) -> Error {
Error(contents)
}
}
impl FromError<b::Error> for Error {
fn from_error(b::Error(contents): b::Error) -> Error {
Error(contents)
}
}
Ok, it works up until that time. And now I need to write something like this:
fn another_func() -> Result<(), Error> {
let _ = try!(<usize as std::str::FromStr>::from_str("14"));
Ok(())
}
And here a problem raises, because there is no conversion from num::ParseIntError to Error. So it seems that I have to implement it again. But why should I? There is a conversion implemented already from num::ParseIntError to b::Error, and there is also a conversion from b::Error to Error. So definitely there is a clean way for rust to convert one type to another without my explicit help.
So, I removed my impl FromError<b::Error> block and tried this blanket impl instead:
impl<E> FromError<E> for Error where b::Error: FromError<E> {
fn from_error(err: E) -> Error {
let b::Error(contents) = <b::Error as FromError<E>>::from_error(err);
Error(contents)
}
}
And it's even worked! However, I didn't succeed to repeat this trick with a::Error, because rustc started to complain about conflicting implementations:
experiment.rs:57:1: 62:2 error: conflicting implementations for trait `core::error::FromError` [E0119]
experiment.rs:57 impl<E> FromError<E> for Error where a::Error: FromError<E> {
experiment.rs:58 fn from_error(err: E) -> Error {
experiment.rs:59 let a::Error(contents) = <a::Error as FromError<E>>::from_error(err);
experiment.rs:60 Error(contents)
experiment.rs:61 }
experiment.rs:62 }
experiment.rs:64:1: 69:2 note: note conflicting implementation here
experiment.rs:64 impl<E> FromError<E> for Error where b::Error: FromError<E> {
experiment.rs:65 fn from_error(err: E) -> Error {
experiment.rs:66 let b::Error(contents) = <b::Error as FromError<E>>::from_error(err);
experiment.rs:67 Error(contents)
experiment.rs:68 }
experiment.rs:69 }
I can even understand the origin of problem (one type FromError<E> can be implemented both for a::Error and b::Error), but I can't get how to fix it.
Theoretically, maybe this is a wrong way and there is another solution for my problem? Or I still have to repeat manually all errors conversions in every new module?
there is no conversion from num::ParseIntError to Error
It does seem like you doing the wrong thing, conceptually. When a library generates an io::Error, like your first example, then it should be up to that library to decide how to handle that error. However, from your question, it sounds like you are generating io::Errors somewhere else and then wanting to treat them as the first library would.
This seems very strange. I wouldn't expect to hand an error generated by library B to library A and say "wrap this error as if you made it". Maybe the thing you are doing should be a part of the appropriate library? Then it can handle the errors as it normally would. Perhaps you could just accept a closure and call the error-conversion as appropriate.
So definitely there is a clean way for Rust to convert one type to another without my explicit help.
(Emphasis mine). That seems really scary to me. How many steps should be allowed in an implicit conversion? What if there are multiple paths, or even if there are cycles? Having those as explicit steps seems reasonable to me.
I can even understand the origin of problem [...], but I can't get how to fix it.
I don't think it is possible to fix this. If you could implement a trait for the same type in multiple different ways, there's simply no way to pick between them, and so the code is ambiguous and rejected by the compiler.