`ExitStatus::exit_code`

Open
#940 2 comments 2 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
5/5
Estimated time
Over a week
Newbie friendliness
35/100
Issue type
Feature
Clarity
Clearly specified
Activity status
Active
Tech stack
rust
Domain
api, backend

Research direction

This is an API change proposal for Rust’s standard library, with no implementation files or tests identified. Read the feature lifecycle guidance and linked Zulip discussion first; the issue says the libs-api team must review the proposal before implementation. Done would require an accepted API design, followed by implementation and tests in a later contribution.

Written by the indexing model from the issue text.

Description

api-change-proposal T-libs

Proposal

Problem statement

There's no good way to convert an ExitStatus to an ExitCode cross-platform. More generally, there's no way to pass on a c_int exit code without knowing platform details.

Motivating examples or use cases

use std::process::{Command, ExitCode};
fn main() -> ExitCode {
    let status = Command::new("foo").status().expect("failed to run command");
    if status.success() {
        ExitCode::SUCCESS
    } else {
        eprintln!("running foo failed: {status:?}");
        if let Some(code) = status.code() {
            // I sure hope this is right for all platforms...
            cfg_select! {
                windows => {
                    use std::os::windows::process::ExitCodeExt;
                    ExitCode::from_raw(code)
                }
                _ => ExitCode::from(code as u8),
            }
        } else {
            ExitCode::FAILURE
        }
    }
}

Solution sketch

Provide an alternative to ExitStatus::code that returns an ExitCode type.

impl ExitStatus {
    /// The same as `ExitStatus::code` except that it returns an `ExitCode` instead of an `i32`.
    pub fn exit_code(&self) -> Option<ExitCode>;
}

Rewriting the above motivating code:

use std::process::{Command, ExitCode};
fn main() -> ExitCode {
    let status = Command::new("foo").status().expect("failed to run command");
    if status.success() {
        ExitCode::SUCCESS
    } else {
        eprintln!("running foo failed: {status:?}");
        status.exit_code().unwrap_or(ExitCode::FAILURE)
}

I would also propose some way to construct an ExitCode from a c_int. As you may know, C's main is int main so this should make at least some sense for every std target. And even exotic targets must support at least ExitCode::SUCCESS and ExitCode::FAILURE so in the worst case we can fallback to using code != 0 to select the exit code.

impl ExitCode {
    /// Create an `ExitCode` from a `c_int`. This is potentially lossy.
    pub const fn new(code: core::ffi::c_int) -> Self;
}

Though maybe it should have a more evocative name because of the cross-platform compatibility hazard (e.g. from_c_int_lossy). However, status and process::exit use i32 without any fuss. Then again, maybe that was a mistake.

Alternatives

Links and related work

#t-libs > `ExitStatus::exit_code` @ 💬

What happens now?

This issue contains an API change proposal (or ACP) and is part of the libs-api team feature lifecycle. Once this issue is filed, the libs-api team will review open proposals as capability becomes available. Current response times do not have a clear estimate, but may be up to several months.

Possible responses

The libs team may respond in various different ways. First, the team will consider the problem (this doesn't require any concrete solution or alternatives to have been proposed):

  • We think this problem seems worth solving, and the standard library might be the right place to solve it.
  • We think that this probably doesn't belong in the standard library.

Second, if there's a concrete solution:

  • We think this specific solution looks roughly right, approved, you or someone else should implement this. (Further review will still happen on the subsequent implementation PR.)
  • We're not sure this is the right solution, and the alternatives or other materials don't give us enough information to be sure about that. Here are some questions we have that aren't answered, or rough ideas about alternatives we'd want to see discussed.
Dominant language
Rust
Stars
188
Forks
28
Avg merge
23h 50m
Merged PRs (30d)
7

Getting set up

This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from rust-lang/libs-team

All issues in rust-lang/libs-team

Similar issues

More Rust issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.