The documentation of std::process::exit
says:
If a clean shutdown is needed it is recommended to only call this function at a known point where there are no more destructors left to run.
Maybe due to my lack of systems programming background, I have no clue if there are destructors left to run at a specific point and if I should care. The only thing that comes to my mind is pending write operations to a file (or to something else) where it's a good idea to leave things in a clean state.
Anything else to watch out for? I suspect it's not advisable to use in larger, more complex programs, but for small tools it seems convenient.
panic!()
in nearly all casesmain()
function[...] for small tools it seems convenient.
If you want to exit your program due to an unrecoverable error, the recommend way is to panic!()
. This will unwind the stack (run all destructors) and exit the program with additional information (including the message string you can specify).
[...] if there are destructors left to run at a specific point and if I should care.
There are destructors left to run if there are local variables that implement Drop
in your current stack frame or in any stack frame above. A stack frame is a region in the stack memory that holds all local variable of a function call (loosely speaking). After a function exits, all local variables discarded which includes calling the destructor of all variables that implement Drop
.
The probability that some destructor needs to run grows with the depth of your stack ("how many functions were called between main()
and your current stack frame). Therefore it's really hard to reason about this whenever you are not in the main()
function.
When do types implement Drop
? When it's wrong to just ignore them. Consider an i32
: when we exit the function we can just... leave it in stack memory, because it doesn't have any negative side effects (ignoring the special case of data security for a moment). However, there are many types that do need to implement Drop
. Here are a few categories:
Box<T>
, Vec<T>
, HashMap<T>
, ...File
, Socket
, ...Ref
, MutexGuard
, ...Not running the destructor for those has different effects. The first group is probably the most harmless one: we will leak memory. But when you exit your program, the operating system will clean up all this memory anyway. Nearly the same goes for OS resources: file descriptor are usually deleted by the operating system when a program exits.
But there are more reasons to run a destructor. The important point to consider: you often don't know why certain types implement Drop
, but these types do rely on it. Certain unwanted things can happen when you ignore this. Often you won't really notice, but sometimes it can lead to serious problems.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With