What is your opinion?
Ok() => ... ,
Err(e) => {
println!("Stopping...");
panic!("The program stopped.").expect("Aaaaaaahhhhh! Panic() does not work. The program continues. ") ;
}
What is your opinion?
Ok() => ... ,
Err(e) => {
println!("Stopping...");
panic!("The program stopped.").expect("Aaaaaaahhhhh! Panic() does not work. The program continues. ") ;
}
The panic!()
macro will eventually call your program's panic handler, which has a signature of fn(&PanicInfo) -> !
. .
Your code also doesn't compile because a panic!()
expression has the type, !
, not Result
.
error[E0599]: no method named `expect` found for type `!` in the current scope
--> src/main.rs:6:40
|
6 | panic!("The program stopped.").expect("Aaaaaaahhhhh! Panic() does not work. The program continues. ") ;
| ^^^^^^ method not found in `!`
I know . It is just an illustration for question ''Should program continue when paanic() fails?''
It is not possible for a panic to "fail". While there are several ways a panic could fail to unwind, those all lead to an infallible abort. (minus OS/cpu bugs, but in that case you have worse things to worry about)
I don't understand what it is that you're asking. Do you care to elaborate?
I think the question is: Can a panic go wrong and make the program coninue, or can you be sure that no command ia executed after panic!
.
I would say: Unless you have some unsafe
code which leads to undefined behavior (where anything could happen), it is ensured that panic!
does not return (and the compiler can use that knowledge).
However panic!
does not immediately terminate the thread. There will be clean up work executed (destructors and drop handlers). If these panic, the program usually aborts, if I understand right. In no case will program flow continue after the first panic. (Unless there is undefined behavior caused by some unsafe
code, in which case anything could happen anyway.)
struct Zombie {
last_words: String,
}
impl Drop for Zombie {
fn drop(&mut self) {
println!("{}", self.last_words);
//panic!(); // This will *not* print "This will never happen."
}
}
fn main() {
let _zombie = Zombie {
last_words: String::from("I can walk and talk!"),
};
println!("Panicking now...");
panic!("PANIC!");
println!("This will never happen.");
}
Output:
Panicking now...
I can walk and talk!
Errors:
Compiling playground v0.0.1 (/playground)
warning: unreachable statement
--> src/main.rs:18:5
|
17 | panic!("PANIC!");
| ---------------- any code following this expression is unreachable
18 | println!("This will never happen.");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unreachable statement
|
= note: `#[warn(unreachable_code)]` on by default
= note: this warning originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
warning: `playground` (bin "playground") generated 1 warning
Finished dev [unoptimized + debuginfo] target(s) in 1.35s
Running `target/debug/playground`
thread 'main' panicked at 'PANIC!', src/main.rs:17:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
That depends on definition of "failing".
This example will never finish unwinding and never cause an abort (but only be killed by Playground due to too-long execution time):
struct Invincible;
impl Drop for Invincible {
fn drop(&mut self) {
loop {
std::thread::sleep(std::time::Duration::from_millis(500));
println!("A mere panic will never kill me, Mwahahaha!")
}
}
}
fn main() {
let _invincible = Invincible;
println!("Panicking now...");
panic!("PANIC!");
}
Output:
Panicking now...
A mere panic will never kill me, Mwahahaha!
A mere panic will never kill me, Mwahahaha!
A mere panic will never kill me, Mwahahaha!
A mere panic will never kill me, Mwahahaha!
A mere panic will never kill me, Mwahahaha!
A mere panic will never kill me, Mwahahaha!
A mere panic will never kill me, Mwahahaha!
A mere panic will never kill me, Mwahahaha!
A mere panic will never kill me, Mwahahaha!
A mere panic will never kill me, Mwahahaha!
A mere panic will never kill me, Mwahahaha!
A mere panic will never kill me, Mwahahaha!
A mere panic will never kill me, Mwahahaha!
A mere panic will never kill me, Mwahahaha!
A mere panic will never kill me, Mwahahaha!
A mere panic will never kill me, Mwahahaha!
A mere panic will never kill me, Mwahahaha!
A mere panic will never kill me, Mwahahaha!
Errors:
Compiling playground v0.0.1 (/playground)
Finished dev [unoptimized + debuginfo] target(s) in 0.58s
Running `target/debug/playground`
thread 'main' panicked at 'PANIC!', src/main.rs:15:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
/playground/tools/entrypoint.sh: line 11: 9 Killed timeout --signal=KILL ${timeout} "$@"
Another weird example, which is certainly not idiomatic Rust:
struct Restarter;
impl Drop for Restarter {
fn drop(&mut self) {
if std::thread::panicking() {
println!("Oh oh, a panic, let's start over!");
main();
}
}
}
fn main() {
let _restarter = Restarter;
if std::thread::panicking() {
println!("... but we're still panicking.");
}
println!("Hello World!");
panic!("PANIC!");
}
Output:
Hello World!
Oh oh, a panic, let's start over!
... but we're still panicking.
Hello World!
Errors:
Compiling playground v0.0.1 (/playground)
Finished dev [unoptimized + debuginfo] target(s) in 0.59s
Running `target/debug/playground`
thread 'main' panicked at 'PANIC!', src/main.rs:18:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
thread 'main' panicked at 'PANIC!', src/main.rs:18:5
stack backtrace:
0: 0x563dced5a500 - std::backtrace_rs::backtrace::libunwind::trace::h32eb3e08e874dd27
at /rustc/897e37553bba8b42751c67658967889d11ecd120/library/std/src/../../backtrace/src/backtrace/libunwind.rs:93:5
…
thread panicked while panicking. aborting.
timeout: the monitored command dumped core
/playground/tools/entrypoint.sh: line 11: 8 Aborted timeout --signal=KILL ${timeout} "$@"
I like this thread a lot. I would have bet some money on "uncatched panic will abort your program in some sense", and now I learn that - of course - it is quite easy to make whatever I want happen
Not exactly.
What @jbe is pointing out is that your panic handler (the thing we call as part of the panicking process) can do anything it wants as long as it satisfies the signature, fn(&PanicInfo) -> !
.
By evaluating to the never type, !
, we say that the function will never "return" in the normal sense of the word (i.e. your program can continue executing the next line of code).
Instead, it must "diverge". There are a couple ways a function can diverge, but here are some I can think of off the top of my head,
setjmp()
and longjmp()
to do a goto
across function boundaries (super unsafe!)When a function is marked as divergent, LLVM will deliberately insert an invalid instruction after the instruction for calling the function to make sure the process aborts.
For example, let's look at this silly piece of code:
pub fn never_returns() -> ! {
loop {}
}
fn main() {
never_returns();
}
These are the instructions it generates:
playground::never_returns:
jmp .LBB8_1
.LBB8_1:
jmp .LBB8_1 <--- jumps back to itself (infinite loop)
playground::main:
pushq %rax
callq playground::never_returns
ud2 <--- your process will always abort when it tries
to execute this instruction
That means even if our never_returns()
function could somehow return, the process would abort because we'd execute ud2
immediately afterwards.
Now mentally replace never_returns()
with your panic handler, and you'll see that it's impossible to continue the normal flow of execution after a panic!()
.
Yes, I understand that. My point is more: I'm always surprised what kind of freedom is implied by low-level programming. I guess it just means "everything is possible". And I'm so used in thinking limited that this surprises me over and over again. And I'm quite happy about this
So, please continue, compose simply ideas in simply ways to achive surprising results
This is only allowed if the stack is never deallocated and in that case is effectively equivalent to getting stuck in an infinite loop. Deallocating the stack without unwinding is not allowed as that would make stack pinning unsound.
True, but I'm sure someone could find a way to use low-level APIs to do it anyway.
Interestingly, I tried calling pthread_exit()
in the playground directly and we unwound the stack like normal, so it looks like pthread_exit()
is built on top of an exception which can't be caught with std::panic::catch_unwind()
. That would line up with this StackOverflow answer about catch (...)
in C++, too.
pthread_exit
uses forced unwinds. It is UB to do a forced unwind through a stack frame containing locals that need to be dropped. See https://rust-lang.github.io/rfcs/2945-c-unwind-abi.html#forced-unwinding
If what you want is for the program to abort on panic, instead of unwiding, just add panic = "abort"
to your Cargo.toml.
This is documented in the official rust book: Unrecoverable Errors with panic! - The Rust Programming Language
No, it's a bit more horrifying. Glibc looks for libgcc.so and if it can find it just calls _Unwind_ForcedUnwind.
There are no exception to catch, stack is just unwond forcibly.
This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.