Is there a macro to convert an Error to Panic? - error-handling

Is there a macro that will convert an error into a panic, similar to the try macro? Do I need to define my own?
For example I'd like to panic if a unit test can't open a file. My current workaround is this:
macro_rules! tryfail {
($expr:expr) => (match $expr {
result::Result::Ok(val) => val,
result::Result::Err(_) => panic!(stringify!($expr))
})
}
#[test]
fn foo() {
let c = tryfail!(File::open(...));
}

This is exactly what the methods Result::unwrap and Result::expect do.
I know you are asking for a macro, but I think your use case can be fulfilled with the unwrap method:
#[test]
fn foo() {
let c = File::open(...).unwrap();
// vs
let c = tryfail!(File::open(...));
}
Note that in code that is not test, it's more idiomatic to use expect.
If you really want a macro, you can write one using unwrap.

Related

My rust macro doesn't want to accept this let statement

I've been trying to make a macro that functions similar to Python's input function.
Rather than write the stdin completely every time I wanted to automate it somewhat, and combine println! so I could kill 2 birds with one stone.
Essenitally, if someone passes in an argument it'll print a string then ask for input, if they don't it will just ask for input from the terminal.
#[macro_export]
macro_rules! input {
($a:expr) => {
println!("{}", $a);
let mut input = String::new();
std::io::stdin().read_line(&mut input).unwrap();
return $input
};
(_) => {
let mut input = String::new();
std::io::stdin().read_line(&mut $input).unwrap();
return $input
};
}
I keep getting an error on the let statement and just don't know how to continue because I don't know the macro syntax well.
I posted the whole code block beacuse on the second match expression I was trying to make a match for when there were no arguments but i'm not sure if I did it correctly.
Sometimes the error messages brought me to github pages and I encounter randomed bugs, so i'm just confused how to continue furhter
It would be most appreciated if someone could help me fix the let statement, and i'd like to apologize for any inconvience.
#[macro_export]
macro_rules! input {
($a:expr) => {{
println!("{}", $a);
let mut input = String::new();
std::io::stdin().read_line(&mut input).unwrap();
input
}};
(_) => {{
let mut input = String::new();
std::io::stdin().read_line(&mut $input).unwrap();
input
}};
}
fn main() {
let foo = input!("Blargh");
}
You need the extra curly braces. The first pair is to enclose what the macro should expand to. The second pair is because the macro should expand to a code block.
Then we get rid of return because a macro isn't a function. Instead, it just expands to some code, and the value of a code block is just the last expression (without a ;).

How to work with custom string errors in rust? [duplicate]

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.

How to match custom Fails with the failure crate

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

How can I silently catch panics in QuickCheck tests?

In the tests of my overflower_support crate, I have found that I get a lot of spurious reports of panics which are already handled using std::panic::catch_unwind(_). This is a bit unfortunate, as it obscures the real errors that may happen. The messages look like:
thread 'safe' panicked at 'arithmetic overflow', src/lib.rs:56
To quell those distracting messages, I introduced the dont_panic(..) function, which hijacks the panic handler, calls a closure and resets the panic handler when done, returning the closures result. It looks like this:
fn dont_panic<F, A, R>(args: A, f: F) -> R
where F: Fn(A) -> R
{
let p = panic::take_hook();
panic::set_hook(Box::new(|_| ()));
let result = f(args);
panic::set_hook(p);
result
}
However, using this function within the function to check somewhat surprisingly not only quells the desired messages, but also quickcheck's error output, which is obviously valuable to me. This occurs even when limiting tests to one thread.
#[test]
fn test_some_panic() {
fn check(x: usize) -> bool {
let expected = if x < 256 { Some(x) } else { None };
let actual = dont_panic(|| panic::catch_unwind(|| { assert!(x < 256); x }).ok());
expected == actual
}
quickcheck(check as fn(usize) -> bool);
}
How can I hide the caught panics from my code while keeping QuickCheck's panics visible?
The default panic handler is printing panic information unconditionally on stderr.
You want to register your own handler.
I've met the same problem and a few others, and I ended up writing a crate to solve them:
panic-control
With it, your example might be solved by running in a "quiet" thread (assuming you weren't interested in using catch_unwind specifically):
use panic_control::spawn_quiet;
#[test]
fn test_some_panic() {
fn check(x: usize) -> bool {
let expected = if x < 256 { Some(x) } else { None };
let h = spawn_quiet(|| { assert!(x < 256); x });
let actual = h.join().ok();
expected == actual
}
quickcheck(check as fn(usize) -> bool);
}
There were two problems with my approach:
The tests run in parallel (and quickcheck appears to add some parallelism of
its own, as -j 1 appears ineffective to quell the panic messages).
The message gets written (or otherwise suppressed by set_hook(_)) no
matter if there's a catch_unwind(_) or not.
However, dpc.pw's idea to distinguish based on files in the panic handler was
spot-on. I changed my approach to call an install_handler() function before
calling quickcheck(_), which I reproduce here in full:
use std::panic;
use std::sync::{Once, ONCE_INIT};
static HANDLER : Once = ONCE_INIT;
fn install_handler() {
HANDLER.call_once(|| {
let p = panic::take_hook();
panic::set_hook(Box::new(move|info| {
if info.location().map_or(false, |l| l.file() != "src/lib.rs" &&
!l.file().ends_with("/num/mod.rs")) {
p(info);
}
}));
})
}
This will quell the panic messages if the panic came from src/lib.rs (which
is my overflower_support code) or somewhere from /num/mod.rs (because the
Rust libcore code may panic, too).
Note that you could omit the Once, but this would add the handler multiple
times and increase the size of stack traces considerably while exacerbating
test performance.

How to store a process in rust

I'd like to store a std::io::process::Process inside a struct in rust. However, the program goes defunct as soon as I pass the Process instance to the struct. Why is that? Code:
use std::io::process::{Command, Process};
use std::rc::Rc;
use std::cell::RefCell;
struct DzenInst {
process: Rc<RefCell<Process>>
//stdin: std::io::pipe::PipeStream
}
impl DzenInst {
// Write a string to the stdin of dzen
fn write(&mut self, s : String) {
let mut stdin = self.process.borrow_mut().stdin.take().unwrap();
println!("Writing to dzen inst");
match stdin.write_str((s + "\n").as_slice()) {
Err(why) => panic!("couldn't write to dzen stdin: {}", why.desc),
Ok(_) => println!("Wrote string to dzen"),
};
}
}
fn CreateDzen() -> DzenInst {
DzenInst {process: Rc::new(RefCell::new(
match Command::new("dzen2").spawn() {
Err(why) => panic!("couldn't spawn dzen: {}", why.desc),
Ok(process) => process,
}))}
}
fn main() {
let mut d1 = CreateDzen();
d1.write("Test".to_string());
std::io::timer::sleep(std::time::duration::Duration::seconds(1));
}
If I write to the process stdin immediately inside CreateDzen, then it works just fine (i.e. the program does not go defunct). I'm assuming that copying the Process instance is causing its destructor to be invoked, which closes the process. Any ideas how to store the Process instance without causing the process to go defunct?
Your Rust code is fine. The problem here is dzen2. You need to add the -p flag to make dzen2 persist EOF (which is in the dzen2 README).
Instead of
match Command::new("dzen2").spawn() {
Use
match Command::new("dzen2").arg("-p").spawn() {