Am I forced to create my own Error type? - error-handling

I want to write a get_members function that returns members from a GitHub team.
pub fn get_members(group_id: &str) -> Result<Vec<User>, Error> {
let client = Client::new();
let query = format!("https://api.github.com/teams/{}/members?access_token={}",
group_id,
config::get_env(config::ENV_TOKEN));
println!("{}", query);
let mut res = try!(client
.get(&query)
.header(UserAgent("my/app".to_owned()))
.send());
let mut body = String::new();
try!(res.read_to_string(&mut body));
try!(json::decode(&body));
}
There are two different types of errors into play. One is hyper::error::Error and the other is rustc_serialize::json::DecoderError.
I thought I could just use implement From<::hyper::error::Error> for Error and From<rustc_serialize::json::DecoderError>. But since neither io::Error nor one of the other two errors is in my crate I'm not allowed to follow that approach.
I wonder what's the way to go here. Do I need to come up with my own AppError Type and then implement the From<> trait for that? Is that the way to go?

Usually yes, using your own error type is the way to go. There are even several crates (of which I was able to find only this one now) which help you to remove the boilerplate. This approach should also be used when you're writing a library, as opposed to an application.
There is an option, however, of using Box<Error> trait object as your error type. Lots of error types in Rust and in third-party libraries implement this trait; therefore, using Result<..., Box<Error>> as a return type should work almost always.

Related

Does Rust have hooks for early return on errors?

panic! allows the setting of a custom (albeit global) hook. Is there anything comparable for early returns with the ? operator? I have a function that needs to close some resources in a special way before exiting. I could write a function ok_or_close() that closes the resources before returning the error:
fn opens_resources() -> Result<(), MyError> {
//Opens some stuff.
//Now a bunch of functions that might raise errors.
ok_or_close(foo(), local variables)?;
ok_or_close(bar(), local variables)?;
ok_or_close(baz(), local variables)?;
ok_or_close(Ok(()), local variables)
}
But that seems verbose. What I'd really like to do is this:
fn opens_resources() -> Result<(), MyError> {
//Opens some stuff.
//Now a bunch of functions that might raise errors.
foo()?;
bar()?;
baz()?;
on_err:
//Closes some stuff. Would prefer not to make
// this a function, uses many local variables.
Ok(())
}
Is there a way to do this or a pattern of programming that gets around this?
The closest thing to this would be the Try trait which allows you to implement how ? affect a specific type, but sadly it is still a nightly experiment as stated here
If you're interested in this features I'd recommend you give a +1 at this issue

How do I turn a glob::GlobError into an io::Error in Rust?

I have a glob::GlobError from the glob crate. There's an underlying io::Error that I need. How do I get it? There's a few methods available, such as this one:
fn error(&self) -> &io::Error
However, it just returns a reference. I need to own it, since I want to put it into another error struct that requires ownership.
An other options is this:
fn cause(&self) -> Option<&std::error::Error>
Same problem with the reference, and on top it's the wrong error type.
It it possible to get an io::Error somehow?
You're looking at outdated documentation.
Go to the latest version, there's a pub fn into_error(self) -> Error.

Alternative to the try (?) operator suited to iterator mapping

In the process of learning Rust, I am getting acquainted with error propagation and the choice between unwrap and the ? operator. After writing some prototype code that only uses unwrap(), I would like to remove unwrap from reusable parts, where panicking on every error is inappropriate.
How would one avoid the use of unwrap in a closure, like in this example?
// todo is VecDeque<PathBuf>
let dir = fs::read_dir(&filename).unwrap();
todo.extend(dir.map(|dirent| dirent.unwrap().path()));
The first unwrap can be easily changed to ?, as long as the containing function returns Result<(), io::Error> or similar. However, the second unwrap, the one in dirent.unwrap().path(), cannot be changed to dirent?.path() because the closure must return a PathBuf, not a Result<PathBuf, io::Error>.
One option is to change extend to an explicit loop:
let dir = fs::read_dir(&filename)?;
for dirent in dir {
todo.push_back(dirent?.path());
}
But that feels wrong - the original extend was elegant and clearly reflected the intention of the code. (It might also have been more efficient than a sequence of push_backs.) How would an experienced Rust developer express error checking in such code?
How would one avoid the use of unwrap in a closure, like in this example?
Well, it really depends on what you wish to do upon failure.
should failure be reported to the user or be silent
if reported, should one failure be reported or all?
if a failure occur, should it interrupt processing?
For example, you could perfectly decide to silently ignore all failures and just skip the entries that fail. In this case, the Iterator::filter_map combined with Result::ok is exactly what you are asking for.
let dir = fs::read_dir(&filename)?;
let todos.extend(dir.filter_map(Result::ok));
The Iterator interface is full of goodies, it's definitely worth perusing when looking for tidier code.
Here is a solution based on filter_map suggested by Matthieu. It calls Result::map_err to ensure the error is "caught" and logged, sending it further to Result::ok and filter_map to remove it from iteration:
fn log_error(e: io::Error) {
eprintln!("{}", e);
}
(|| {
let dir = fs::read_dir(&filename)?;
todo.extend(dir
.filter_map(|res| res.map_err(log_error).ok()))
.map(|dirent| dirent.path()));
})().unwrap_or_else(log_error)

Reuse the description of an existing Error when creating a new Error

I have the following code in Rust, which does not compile, but shows the intent of what I'd like to do.
pub fn parse(cursor: &mut io::Cursor<&[u8]>) -> io::Result<Ack> {
use self::byteorder::{BigEndian, ReadBytesExt};
use self::core::error::Error;
match cursor.read_u16::<BigEndian>() {
Err(byteorder::Error::Io(error)) => Err(error),
Err(error) =>
Err(io::Error::new(io::ErrorKind::Other, error.description(),
None)),
Ok(value) => Ok(Ack { block_number: value })
}
}
Essentially, I want to take the error description of an error returned by the byteorder library and use it to create the description of an error I'll pass back to the user of my library. This fails with packets.rs:166:58: 166:63 error:errordoes not live long enough, and I understand why.
The byteorder library solves this issue by wrapping an std::io::Result in the byteorder::Error::Io constructor. However, I don't want to take this route because I'd have to define my own error type that wraps either an std::io::Error or a byteorder::Error. It seems to me that my users shouldn't know or care that I use the byteorder library, and it shouldn't be part of my interface.
I'm a Rust newbie and don't yet know the idioms and best practices of the language and design. What are my options for dealing with this?
Your problem is in fact in that io::Error::new()'s second parameter is &'static str, while byteorder::Error::description() returns a &'a str where 'a is lifetime of the error object itself which is less than 'static. Hence you can't use it for io::Error's description.
The simplest fix would be moving byteorder::Error description to detail field of io::Error:
Err(error) =>
Err(io::Error::new(
io::ErrorKind::Other,
"byteorder error",
Some(error.description().to_string())
)),
However, you should seriously consider making a custom wrapper error type which encapsulates all "downstream" errors. With properly written FromError instances you should be able to write something like
try!(cursor.read_u16::<BigEndian>()
.map(|value| Ack { block_number: value }))
instead of your whole match. Custom error wrappers will also help you when your program grows and more "downstream" error sources appear - you could just add new enum variants and/or FromError implementations to support these new errors.
I cannot test your code so I can't be sure. Isn't the ref keyword enough?
Err(byteorder::Error::Io(ref error)) => Err(error),

Can I write tests for invalid lifetimes?

I'm writing some Rust code that manipulates raw pointers. These raw pointers are then exposed to users through structures that use ContravariantLifetime to tie the lifetime of the struct to my object.
I'd like to be able to write tests that validate that the user-facing structures cannot live longer than my object. I have code like the following:
fn element_cannot_outlive_parts() {
let mut z = {
let p = Package::new();
p.create() // returns an object that cannot live longer than p
};
}
This fails to compile, which is exactly what I want. However, I'd like to have some automated check that this behavior is true even after whatever refactoring I do to the code.
My best idea at the moment is to write one-off Rust files with this code and rig up bash scripts to attempt to compile them and look for specific error messages, which all feels pretty hacky.
The Rust project has a special set of tests called "compile-fail" tests that do exactly what you want.
The compiletest crate is an extraction of this idea that allows other libraries to do the same thing:
fn main() {
let x: (u64, bool) = (true, 42u64);
//~^ ERROR mismatched types
//~^^ ERROR mismatched types
}
One idea that gets halfway there is to use Cargo's "features".
Specify tests with a feature flag:
#[test]
#[cfg(feature = "compile_failure")]
fn bogus_test() {}
Add this to Cargo.toml:
[features]
compile_failure = []
And run tests as
cargo test --features compile_failure
The obvious thing missing from this is the automatic checking of "was it the right failure". If nothing else, this allows me to have tests that are semi-living in my codebase.
You are able to annotate a test that you expect to fail.
#[should_fail]
As such, you can write a test that attempts to breach the life time it should have, and thus fail, which would actually be a pass.
For an example of a test for 'index out of bounds' see below (pulled from the Rust guides)
#[test]
#[should_fail]
fn test_out_of_bounds_failure() {
let v: &[int] = [];
v[0];
}
I believe that this example would be a compilation error, so it would stand to reason your compile lifetime violation error would be caught by this too.