Introduce unwrap_debug methods in Result and Option

Hello, Team

I want propose method like unwrap_debug and unwrap_debug_else if release mode execute code in closure F and unwrap_debug_else_default Unwap in debug but in release mode provide the default value and other... for Unwrapping only in debug but in release mode not do anything, This effort for improve perfomance

In release mode you likely still want some kind of logging if an unwrap fails. See Re-opening deprecating Option::unwrap and Result::unwrap - #54 by DragonDev1906

1 Like

I think a debug_assert_some/debug_assert_ok (or perhaps simply debug_assert) method which turns into the identity function in prod would be a cleaner approach.

So you could write something like option.debug_assert().unwrap_or(default).

So instead of silent behavior in release mode, We i will do usage like this

let s: i32 = None;
s.unwrap_debug(|| {
    eprintln("This is a critical bug, We not detect it in debug mode I appreciate to report it to US");
    exit(253); // Example 123 error code indicate a critical bug
});

The function take only one argument the closer to be executed in release mode, and panic like unwrap in debug mode

And for expect_debug will be like this

let s: i32 = None;

s.expect_debug("Variable is none"  , || {
    eprintln("This is a critical bug, We not detect it in debug mode I appreciate to report it to US");
    exit(253); // Example 123 error code indicate a critical bug
});

This function take the message like expect in debug mode and the second argument the closure to be executed in release mode instead of silent behavior.

I know it isn't really what you're here about, but please avoid using exit codes > 127 on Unix. They're reserved for things like "exited due to signal". WIFSIGNALED will return "true" on Linux, at least, for such an exit code.

1 Like

I'm having trouble understanding what you are asking, but this is simple enough to implement that I don't think it needs to live in std.

I wrote an unwrap-log crate that sounds superficially similar to what you are asking for. In hindsight maybe I should have used tracing and not log...

Hello, @mathstuf

I fixed this from 253 to 123.

Why not 101? That is what we use for panics already.

This is a common misconception. In wait statuses, exit codes greater than 127 are correctly distinguished from exit-due-to-signal. Demo: (Regrettably, I know how to do this off the top of my head in C but not in Rust.)

#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
int main(void)
{
    pid_t child = fork();
    if (child < 0) {
        perror("fork");
        return 1;
    } else if (child == 0) {
        return 253;
    } else {
        int s;
        if (waitpid(child, &s, 0) < 0) {
            perror("waitpid");
            return 1;
        }
        if (WIFEXITED(s)) {
            printf("WIFEXITED(s) = 1 WEXITSTATUS(s) = %d\n", WEXITSTATUS(s));
        } else if (WIFSIGNALED(s)) {
            printf("WIFSIGNALED(s) = 1 WTERMSIG(s) = %d\n", WTERMSIG(s));
        } else {
            printf("Unusual wait status %04x\n", s);
        }
        return 0;
    }
}

This will print WIFEXITED(s) = 1 WEXITSTATUS(s) = 253 on every Unix that has waitpid.


The misconception comes from the behavior of the Bourne shell's special variable $?. If you run a program from sh and it exits due to a signal, $? will be set to 128 + the signal number. Thus, in shell scripts exit codes greater than 127 are indistinguishable from signal exits.

The shell does this because it wants to smush signal exit codes into the space of values that can be passed through exit and waitpid, so that a signal exit notification can be easily propagated upward from a child of a subshell all the way to the outermost shell.

It is bad practice to use exit codes greater than 127 for any other purpose than matching this shell convention, but the shell convention is the reason, not a kernel-level limitation.

3 Likes

Interesting. I was looking at bits/waitstatus.h and I see now that other macros do look at the second byte of status, so there is room there at least. And yes, a lot of my knowledge in this area comes from doing glue in bash :slight_smile: .

This can arguably be even worse than an .unwrap(). std::process::exit will terminate the whole process as opposed to unwinding the current thread, which runs stack destructors and can be catched. In a web server this can make the difference between never responsing to all pending user requests (leaving them to timeout) and responding to the current one with a HTTP 500 error code.

Hello, @SkiFire13

Your concern is correct, So the idea is not good.