rust-lang / rust-lang/libs-team

std::os::unix::env::{argc, argv}

Open
#348 12 comments 3 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

api-change-proposal
Dominant language
Rust
Stars
178
Forks
28
Avg merge
15m
Merged PRs (30d)
1

Description

Proposal

Problem statement

When making FFI calls from Rust on UNIX targets, it's common to need NUL-terminated UTF-8 strings. The same is true of NUL-terminated Widechar strings on Windows FFI calls. If these strings are obtained from environment variables or process arguments, on both UNIX and Windows targets, they already exist in the required format in the process's memory. Unfortunately, in today's Rust, there is no way in std to access these in their original formats without paying for heap allocations, traversals, and/or syscalls.

Today in std the only ways to access these values are via VarsOs and ArgsOs, both of which are iterators over OsString values. These strings are not in the original format; they have been reallocated and had their NUL terminators dropped, meaning that further allocations and conversions are necessary to get them back into their original form.

On Windows, these allocations and conversions can be avoided through an unsafe direct FFI call to GetCommandLineW. There is an equivalent for this on some UNIX systems (e.g. macOS) but on others, there is no direct FFI call which exposes these. The only way to access them is through syscalls like reading /proc/self/cmdline on Linux or sysctlbyname on FreeBSD.

Motivating examples or use cases

I have a command-line application which:

  • Reads command-line arguments and environment variables to decide what operation to do
  • Also commonly reads filenames from both command-line arguments and filenames
  • Also commonly passes those filenames directly to OS API calls which expect them in the original format (NUL-terminated strings in the OS's native encoding)

Today, there is no zero-cost way to access these in Rust; the lowest-cost way that's available on each of these OSes is:

  • GetCommandLineW on Windows
  • NSGetArgc and NSGetArgv on macOS
  • On Linux, read /proc/self/cmdline - this requires a syscall to access
  • On FreeBSD, use sysctlbyname, which also requires a syscall

Solution sketch

Introduce these OS-specific functions to a new module, std::os::unix::env:

fn argc() -> usize;
fn argv() -> *const *const c_char;

These functions would read from these atomics, which is why they do not need to take &self.

Today, these atomics are not exposed, and there is no direct FFI-based workaround to access the values they hold. That's in part because they rely on non-standard link_section extensions. So there's no way to write a crate in userspace for these today.

For symmetry, it would seem reasonable to introduce this function to a new OS-specific module, std::os::windows::env:

fn args_widechar() -> *const *const u16;

This would be implemented as a call to GetCommandLineW, and would only be there for symmetry with the proposed std::os::windows::env, so that Windows programs didn't need to do FFI to do something that UNIX programs could use using std.

Alternatives

These functions could use CStr over *const c_char, but then they would have to be unsafe because CStr requires that the pointers be non-null, which is not a guarantee in this case. Additionally, since the motivation for this is FFI, the CStrs would likely need to be converted into *const c_chars anyway, so overall CStr seems both unsafe and unhelpful here.

It might sound reasonable to have a function which returns a slice instead of separate functions for argc and argv. However, as a comment in the current UNIX args implementation notes, argc is not necessarily an accurate length for argv, meaning that building a safe slice would require traversing the argv until a null pointer is encountered—which would be undesirable given that the motivation for this use case is to avoid overhead.

As an alternative, it could make sense to have an Iterator which iterates over argv until it encounters a null, and uses argc for a size_hint only. Another alternative would be to use Option<NonNull<...>> instead of const *, to emphasize that all the pointers could be nullable. However, in FFI use cases, the FFI APIs will be asking for raw pointers, so having access to the raw pointers is more helpful than having an Option<NonNull<...>> and especially an iterator.

So it seems like the minimal proposal here would be to expose the raw pointers, and then optionally an iterator convenience method could be discussed on top of that.

Links and related work

There are various OS-specific functions in std::os already, like std::os::unix::fs::chown.

Related threads:

Contributor guide

No contributing guide indexed for this repository

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.

Research direction

Start with library/std/src/sys/unix/args.rs and the linked atomics and platform-specific argument handling. Compare the proposed raw-pointer APIs with the existing Windows GetCommandLineW and Unix implementations, including the stated nullability and argc limitations. Done requires a settled API and implementation scope across the affected Unix and Windows targets.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
operating-systems
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.