I need to catch a panic while the program is running and without closing the program. For example, how to catch a panic here and print “Hello, World”?:
fn main() {
let v = vec![1, 2, 3];
v[99];
println!("Hello, World");
}
You can use std::panic::catch_unwind to, well, catch unwinding panics, but do make sure to read the documentation first:
fn main() {
let v = vec![1, 2, 3];
let panics = std::panic::catch_unwind(|| v[99]).is_err();
assert!(panics);
println!("Hello, World");
}
If you don't know that the index is valid at run time, you can use get instead of [] indexing. This will return an option, either Some() if the element exists, or None if it does not:
if let Some(x) = v.get(99) {
}
or:
match v.get(99) {
Some(x) => {
},
None => {
}
}
If this is a more general question and the given snippet is just an example, then the correct answer is you don't -- panics are not for control flow. If you need to recover from an error, you should use a method that returns a Result or Option.
Related
I am very new to Rust and decided my first program to be a brainfuck interpreter.
I plan on using jump tables as the solution for the loops.
However I decided to rewrite the method to make it look better (for my tastes) and i got an error that I can't quite understand why
Code before causes no errors:
fn process_jumps(jump_map: &mut Vec<usize>, instructions: &Vec<Inst>){
let mut stack: Vec<usize> = Vec::new();
for (i, inst) in instructions.iter().enumerate() {
match inst {
Inst::LoopOpen => stack.push(i),
Inst::LoopClose => {
jump_map[i] = stack.pop();
jump_map[jump_map[i]] = i;
}
_ => ()
}
}
}
Code after has an error (marked in code):
fn process_jumps(instructions: &Vec<Inst>) -> Vec<usize> {
let mut jump_table: Vec<usize> = Vec::new();
let mut stack: Vec<usize> = Vec::new();
for (i, inst) in instructions.iter().enumerate() {
match inst {
Inst::LoopOpen => stack.push(i),
Inst::LoopClose => {
jump_table[i] = stack.pop(); // expected `usize`, found `Option<usize>`
jump_table[jump_map[i]] = i;
}
_ => ()
}
}
return jump_table;
}
My main question is why my code before didn't need me to check the optional?
Vec's pop() method returns Option<T>, not T.
You need to get the usize value from inside that Option, just make sure you've handled the None case correctly. When you are sure None is not possible, the simplest thing you could do is to unwrap() it.
Neither of your examples should really compile, as they both try to assign Option<usize> to a Vec<usize>.
I have a Rust program where I spawn a number of threads and store the JoinHandles. When I later join the threads that panic!ed, I'm not sure how to retrieve the message that was passed to panic!
Recovering from `panic!` in another thread covers a simple case, but when I applied it to my code it turns out it doesn't work, so I have updated my example:
If I run the following program
fn main() {
let handle = std::thread::spawn(||panic!(format!("demo {}",1)));
match handle.join() {
Ok(_) => println!("OK"),
Err(b) => println!("Err {:?}", b.downcast_ref::<&'static str>())
}
}
(rust playground)
It prints Err None . I want to access the "demo" message.
Based on the comments, I was able to come up with a working solution (rust playground):
fn main() {
let handle = std::thread::spawn(|| {
panic!("malfunction {}", 7);
if true {
Err(14)
} else {
Ok("bacon".to_string())
}
});
match handle.join() {
Ok(msg) => println!("OK {:?}", msg),
Err(b) => {
let msg = if let Some(msg) = b.downcast_ref::<&'static str>() {
msg.to_string()
} else if let Some(msg) = b.downcast_ref::<String>() {
msg.clone()
} else {
format!("?{:?}", b)
};
println!("{}", msg);
}
}
}
which prints malfunction 7 like I want. While this toy example doesn't need all the branches, a more complex piece of code might have some panic!s that give &'static str and others that give String.
I have a Stream that generates Result's, I want to apply a try_filter_map() on these items and take the first item that does not result in an error.
Consider the following example: (Playground)
use futures::{pin_mut, prelude::*};
use tokio;
#[tokio::main]
async fn main() {
let s = stream::iter(vec![Ok(1), Ok(2), Ok(3)])
.try_filter_map(|v| async move { if v == 1 { Err("Ohno!") } else { Ok(Some(v)) } })
.inspect_err(|err| println!("Error {:?}", err))
.filter_map(|r| future::ready(r.ok()));
pin_mut!(s);
let result = s.next().await;
println!("{:?}", result);
}
When I run this code I receive the following error:
Error "Ohno!"
thread 'main' panicked at '`async fn` resumed after completion'
I am fairly new to Rust and could use some help to solve this issue.
It seems like this is a bug. Until a new release fixes the issue, it looks like you are using try_filter_map without actually filtering the stream? If that is the case, you can use and_then instead:
let s = stream::iter(vec![Ok(1), Ok(2), Ok(3)])
.and_then(|v| future::ready(if v == 1 { Err("Ohno!") } else { Ok(v) }))
.inspect_err(|err| println!("Error {:?}", err))
.filter_map(|r| future::ready(r.ok()));
I have working example of a simple loop (mostly taken from the odbc crate's example):
use std::io;
use odbc::*;
use odbc_safe::AutocommitOn;
fn main(){
let env = create_environment_v3().map_err(|e| e.unwrap()).unwrap();
let conn = env.connect_with_connection_string(CONN_STRING).unwrap();
let mut stmt = Statement::with_parent(&conn).unwrap();
loop {
let mut sql_text = String::new();
println!("Please enter SQL statement string: ");
io::stdin().read_line(&mut sql_text).unwrap();
stmt = match stmt.exec_direct(&sql_text).unwrap() {
Data(mut stmt) => {
let cols = stmt.num_result_cols().unwrap();
while let Some(mut cursor) = stmt.fetch().unwrap() {
for i in 1..(cols + 1) {
match cursor.get_data::<&str>(i as u16).unwrap() {
Some(val) => print!(" {}", val),
None => print!(" NULL"),
}
}
println!();
}
stmt.close_cursor().unwrap()
}
NoData(stmt) => {println!("Query executed, no data returned"); stmt}
}
}
}
I don't want to create new Statements for each query, as I just can .close_cursor().
I'd like to extract the loop's body to a function, like this:
fn exec_stmt(stmt: Statement<Allocated, NoResult, AutocommitOn>) {
//loop's body here
}
But I just can't! The .exec_direct() method mutably consumes my Statement and returns another. I tried different ways to pass Statement arg to the function (borrow, RefCell, etc), but they all fail when using in a loop. I am still new to Rust, so most likely I just don't know something, or does the .exec_direct's Statement consumption makes it impossible?
There's no nice way to move and then move back values through parameters. It's probably best to copy what .exec_direct does and just make the return type of your function a statement as well.
The usage would then look like this:
let mut stmt = Statement::with_parent(&conn).unwrap();
loop {
stmt = exec_stmt(stmnt);
}
and your function signature would be:
fn exec_stmt(stmt: Statement<...>) -> Statement<...> {
match stmt.exec_direct() {
...
}
}
I probably wouldn't recommend this, but if you really wanted to get it to work you could use Option and the .take() method.
fn exec_stmt(some_stmt: &mut Option<Statement<...>>) {
let stmt = some_stmt.take().unwrap();
// do stuff ...
some_stmt.replace(stmt);
}
The odbc-safe crate tried to have each state transition of ODBC reflected in a different type. The odbc-api crate also tries to protect you from errors, but is a bit more subtle about it. Your use case would be covered by the the Preallocated struct.
The analog example from the odbc-api documentation looks like this:
use odbc_api::{Connection, Error};
use std::io::{self, stdin, Read};
fn interactive(conn: &Connection) -> io::Result<()>{
let mut statement = conn.preallocate().unwrap();
let mut query = String::new();
stdin().read_line(&mut query)?;
while !query.is_empty() {
match statement.execute(&query, ()) {
Err(e) => println!("{}", e),
Ok(None) => println!("No results set generated."),
Ok(Some(cursor)) => {
// ...print cursor contents...
},
}
stdin().read_line(&mut query)?;
}
Ok(())
}
This will allow you to declare a function without any trouble:
use odbc_api::Preallocated;
fn exec_statement(stmt: &mut Preallocated) {
// loops body here
}
I know that in Rust there is no try/catch, and you can't throw a rolling save from the thread that is currently panicking.
I know you should not create and handle errors like this. This is just for example's sake.
However, I am wondering what the best way to recover from a panic is. This is what I have now:
use std::thread;
fn main() {
println!("Hello, world!");
let h = thread::spawn(|| {
thread::sleep_ms(1000);
panic!("boom");
});
let r = h.join();
match r {
Ok(r) => println!("All is well! {:?}", r),
Err(e) => println!("Got an error! {:?}", e)
}
println!("Exiting main!");
}
Is there a better way to handle errors from other threads? Is there a way to capture the message of the panic? This seems to only tell me that the error is of type Any. Thanks!
Putting aside "you should be using Result where possible," yes, this is basically how you catch a panic in Rust. Keep in mind that "recover" is perhaps not the best way of phrasing this in Rust. You don't really recover from panics in Rust, you isolate them, then detect them. There is no On Error Resume Next :P.
That said, there are two things to add to your example. First is how to get at the panic message. The key observation is that Any, in order to be used, must be explicitly downcast to the exact, concrete type it contains. In this case, since the panic message is a &'static str, you need to downcast to that.
The second thing is that there is a new API in nightly called catch_panic that lets you isolate a panic without having to start a thread. That said, it comes with the same restrictions as spawning a new thread: you cannot pass a non-'static reference across the isolation boundary. Note that this is an unstable addition; there are no guarantees about stability yet, and you'll need a nightly compiler to access it.
Here is an example which shows both of those. You can also run this on the Rust Playpen.
#![feature(catch_panic)]
use std::thread;
fn main() {
println!("Hello, world!");
let h = thread::spawn(|| {
thread::sleep_ms(500);
panic!("boom");
});
let r = h.join();
handle(r);
let r = thread::catch_panic(|| {
thread::sleep_ms(500);
panic!(String::from("boom again!"));
});
handle(r);
println!("Exiting main!");
}
fn handle(r: thread::Result<()>) {
match r {
Ok(r) => println!("All is well! {:?}", r),
Err(e) => {
if let Some(e) = e.downcast_ref::<&'static str>() {
println!("Got an error: {}", e);
} else {
println!("Got an unknown error: {:?}", e);
}
}
}
}