# Lack of API mutating args at std::process::Command

**URL:** <https://internals.rust-lang.org/t/lack-of-api-mutating-args-at-std-command/14908>\
**Category:** libs\
**Created:** [June 22, 2021, 4:34pm UTC](https://internals.rust-lang.org/t/lack-of-api-mutating-args-at-std-command/14908 "2021-06-22T16:34:56Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![cehteh](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cehteh/32/8289_2.png) [@cehteh](https://internals.rust-lang.org/u/cehteh)\
**Post date:** [June 22, 2021, 4:34pm UTC](https://internals.rust-lang.org/t/lack-of-api-mutating-args-at-std-command/14908/1 "2021-06-22T16:34:56Z")

</div>

std::process::Command has no way to mutate the supplied arguments when one wants to execute a process again. Sometimes (esp in testsuites) this is required.

The other attributes (environment variables, current\_dir) have API's for mutating them.

Therefore I'd like to propose/discuss what could be done for the commands args.

Traditionally program arguments are an array of strings where `argv[0]` is slightly special as it (usually) contains the program name, it can still be changed but is not part of the arguments handled by the argument parser. std::process::Command will already initialize that for you.

For mutating the argv as bare minimum I'd propose:

```rust
pub fn args_reset()

```

Shall reset the argument vector to the state it was when `Commmand::new()` created it. That is: setting its length to 1 keeping `argv[0]` intact.

Furher (to be discussed) API's may be:

```rust
pub fn arg_set(index: usize, value: &str) -> Result<(), Error>

```

replacing the argument at the given index, results in an error when the index was out of range. This can be used to change `argv[0]`

```rust
pub fn arg_pop() -> Result<(), Error>
// and/or
pub fn args_pop(n: usize) -> Result<(), Error>

```

drops the last element / n last elements returns an Error when there are less elements available. (discussion: shall `argv[0]` be preserved?)

```rust
pub fn args_new<I, S>(&mut self, args: I) -> &mut Command where
    I: IntoIterator<Item = S>,
    S: AsRef<OsStr>,

```

sets a new list of arguments, like `args_reset()` followed by `args()`. leaves argv[0] untouched

---

<div class="post-metadata">

**Author:** ![bjorn3](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/bjorn3/32/2736_2.png) [@bjorn3](https://internals.rust-lang.org/u/bjorn3)\
**Post date:** [June 22, 2021, 4:40pm UTC](https://internals.rust-lang.org/t/lack-of-api-mutating-args-at-std-command/14908/2 "2021-06-22T16:40:00Z")

</div>

Can you recreate the `Command` from scratch?

---

<div class="post-metadata">

**Author:** ![bascule](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/bascule/32/3057_2.png) [@bascule](https://internals.rust-lang.org/u/bascule)\
**Post date:** [June 22, 2021, 7:07pm UTC](https://internals.rust-lang.org/t/lack-of-api-mutating-args-at-std-command/14908/3 "2021-06-22T19:07:53Z")

</div>

It seems like if `std::process::Command` impl'd `Clone`, you could clone it just prior to adding the arguments, and then clone it again if you want to change them. Unfortunately it doesn't.

---

<div class="post-metadata">

**Author:** ![cehteh](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cehteh/32/8289_2.png) [@cehteh](https://internals.rust-lang.org/u/cehteh)\
**Post date:** [June 22, 2021, 7:56pm UTC](https://internals.rust-lang.org/t/lack-of-api-mutating-args-at-std-command/14908/4 "2021-06-22T19:56:46Z")

</div>

Sure, the Command can be recreated from scratch, there are workarounds for this issue. Implementing 'Clone' would do the job too.

The point here is that 'Command' is already a builder and can be used for multiple instances with one of the execution triggering methods. The other attributes (Oops as I am writing this I haven't checked for Stdio) do have API's to refine them before firing one execution again, only the 'args' does not. I'd call this an inconsistency in the API.

IMO it would be valuable to add this, to my understanding it won't cost much and won't break anything, just making things more consistent and easier to use.

---

<div class="post-metadata">

**Author:** ![kornel](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/kornel/32/2711_2.png) [@kornel](https://internals.rust-lang.org/u/kornel)\
**Post date:** [June 22, 2021, 10:43pm UTC](https://internals.rust-lang.org/t/lack-of-api-mutating-args-at-std-command/14908/5 "2021-06-22T22:43:06Z")

</div>

`clear_args()` seems like the simplest thing that could work. More than that goes onto a slippery slope of "why not \< insert any random Vec method \> too?"

---

<div class="post-metadata">

**Author:** ![cehteh](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cehteh/32/8289_2.png) [@cehteh](https://internals.rust-lang.org/u/cehteh)\
**Post date:** [June 23, 2021, 12:22pm UTC](https://internals.rust-lang.org/t/lack-of-api-mutating-args-at-std-command/14908/6 "2021-06-23T12:22:27Z")

</div>

That's why I put other API's up to discussion. A 'reset' function is the bare minimum required (i'd suggest reset instead clear, because it should preserve argv[0]).

Additionally I'd opt for the 'set' function that replaces an existing component by index (dropping or returning the old one). Because it is common that one wants to prepare a command to be executed and then run it over different filenames for example.

---

<div class="post-metadata">

**Author:** ![kornel](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/kornel/32/2711_2.png) [@kornel](https://internals.rust-lang.org/u/kornel)\
**Post date:** [June 23, 2021, 4:57pm UTC](https://internals.rust-lang.org/t/lack-of-api-mutating-args-at-std-command/14908/7 "2021-06-23T16:57:49Z")

</div>

`argv[0]` is specified using `Command::new()`, and can't be set using `.arg()`, so I think it's fine to present an API as if the "args" meant `argv[1..]` only, so `clear()` clears `[1..]`.

Set by index would raise question why not implement `Index` and `command[n] = arg`. And if there's set, why not `remove`, and if there's `remove`, why not `insert`, `retain`, and so on.

---

<div class="post-metadata">

**Author:** ![cehteh](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cehteh/32/8289_2.png) [@cehteh](https://internals.rust-lang.org/u/cehteh)\
**Post date:** [June 23, 2021, 6:19pm UTC](https://internals.rust-lang.org/t/lack-of-api-mutating-args-at-std-command/14908/8 "2021-06-23T18:19:04Z")

</div>

Question is answered by that the 'args' is not implemented as single Vec, nor are all the methods Vec offers needed in this case.

My aim is for an usable ergonomic API. Take a look at 'env' which has `env_clear()` and `env_remove()`.

Having an 'args\_clear()' that clears [1..] is Ok. (while sometimes one wants to chage argv[0]) but that is unrelated to this topic, if need arises this can be discussed again under another topic.

Looking at env, values can be updated with the `env()` and `envs()` method, since that is an associative store. For args it is very common that one wants to update an argument at a certain position. I am thinking this really deserves to have an API, not for efficiency (which is irrelevant when we talking about spawning processes), but for ergonomic reasons with something like:

```rust
let mut command = Command::new("dosomething").args(["FILE"]);
somefiles.iter().for_each(|file| command.arg_set(1, file)?.spawn());

```

There are certain use cases where one wants to remove or insert more arguments, but that may only fatten the API, in that case it is Ok to clean and rebuild the argv from scratch.

---

<div class="post-metadata">

**Author:** ![cehteh](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cehteh/32/8289_2.png) [@cehteh](https://internals.rust-lang.org/u/cehteh)\
**Post date:** [June 23, 2021, 6:31pm UTC](https://internals.rust-lang.org/t/lack-of-api-mutating-args-at-std-command/14908/9 "2021-06-23T18:31:27Z")

</div>

Just some observation:

`arg_set()` should return a `Result<Command, Error>`

Which surprises me now since the `env()` functions don't return a Result but a Command. But there are certain ways which would fail setting an environment variable (illegal characters in its name). Is this intentional or a defect.

---

<div class="post-metadata">

**Author:** ![chrisd](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/chrisd/32/7232_2.png) [@chrisd](https://internals.rust-lang.org/u/chrisd)\
**Post date:** [June 23, 2021, 6:40pm UTC](https://internals.rust-lang.org/t/lack-of-api-mutating-args-at-std-command/14908/10 "2021-06-23T18:40:15Z")

</div>

Any errors will be returned when you call `spawn`, etc.

I assume they aren't tested any earlier because it would have made the builder less ergonomic back before we had the `?` operator.

---

<div class="post-metadata">

**Author:** ![cehteh](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cehteh/32/8289_2.png) [@cehteh](https://internals.rust-lang.org/u/cehteh)\
**Post date:** [June 23, 2021, 6:57pm UTC](https://internals.rust-lang.org/t/lack-of-api-mutating-args-at-std-command/14908/11 "2021-06-23T18:57:22Z")

</div>

Actually errors are not handled in that case: (testing on linux here)

```rust
fn main() {
    use std::process::Command;

    let out = Command::new("bash")
        .env("FOO=BAR", "BAZ")
        .args(["-c", "/bin/echo $FOO"])
        .output()
        .unwrap()
        .stdout;
    println!("Surprise: {}", String::from_utf8(out).unwrap());
}

```

this results in "BAR=BAZ" as if one did `.env("FOO", "BAR=BAZ")` ... which isnt unexpected when you know how the underlying representation in the libc is.....

---

<div class="post-metadata">

**Author:** ![chrisd](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/chrisd/32/7232_2.png) [@chrisd](https://internals.rust-lang.org/u/chrisd)\
**Post date:** [June 23, 2021, 7:02pm UTC](https://internals.rust-lang.org/t/lack-of-api-mutating-args-at-std-command/14908/12 "2021-06-23T19:02:57Z")

</div>

Ah, interesting! I assumed it would do some testing when building the variables but only a null check is done.

---

<div class="post-metadata">

**Author:** ![cehteh](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cehteh/32/8289_2.png) [@cehteh](https://internals.rust-lang.org/u/cehteh)\
**Post date:** [June 23, 2021, 7:12pm UTC](https://internals.rust-lang.org/t/lack-of-api-mutating-args-at-std-command/14908/13 "2021-06-23T19:12:24Z")

</div>

Now I am puzzled about how to fix that. Other OSes would certainly put other restrictions on characters a env var name can hold (and possibly different rules on the value as well). Changing the return into a `Result<>` would be a breaking change, nogo for the stdlib. Validating the env before spawning seems to be costly (ymmw). Possibly one could store the env validation state in Command, dirty it every time the env is mutated and validate it when dirty -\> set it to Ok or Error on spawn time, returning Error perhaps.

---

<div class="post-metadata">

**Author:** ![mathstuf](https://avatars.discourse-cdn.com/v4/letter/m/958977/32.png) [@mathstuf](https://internals.rust-lang.org/u/mathstuf)\
**Post date:** [June 23, 2021, 7:29pm UTC](https://internals.rust-lang.org/t/lack-of-api-mutating-args-at-std-command/14908/14 "2021-06-23T19:29:52Z")

</div>

See [this thread](https://internals.rust-lang.org/t/why-should-std-var-panic/14847) for more on `env` validation.

---

<div class="post-metadata">

**Author:** ![chrisd](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/chrisd/32/7232_2.png) [@chrisd](https://internals.rust-lang.org/u/chrisd)\
**Post date:** [June 23, 2021, 8:05pm UTC](https://internals.rust-lang.org/t/lack-of-api-mutating-args-at-std-command/14908/15 "2021-06-23T20:05:02Z")

</div>

Huh, [Unix Rust](https://github.com/rust-lang/rust/blob/b6f3cb9502b1910f6af32f426fdb78e813b390ef/library/std/src/sys/unix/process/process_common.rs#L318) seems to use a `saw_null` variable as a way to [signal an error](https://github.com/rust-lang/rust/blob/b6f3cb9502b1910f6af32f426fdb78e813b390ef/library/std/src/sys/unix/process/process_unix.rs#L32).

---

<div class="post-metadata">

**Author:** ![cehteh](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cehteh/32/8289_2.png) [@cehteh](https://internals.rust-lang.org/u/cehteh)\
**Post date:** [June 23, 2021, 9:23pm UTC](https://internals.rust-lang.org/t/lack-of-api-mutating-args-at-std-command/14908/16 "2021-06-23T21:23:48Z")

</div>

> [@mathstuf](#):
>
> See [this thread](https://internals.rust-lang.org/t/why-should-std-var-panic/14847) for more on `env` validation.

I think `Command::env()` things should follow whats there (PR) finalized. But I have no idea how to deal with a breaking change in rusts stdlib (feature?). Anyway this is going off-topic here (the 'args' thing). Shall we open a new thread on this forum about the 'env' issue or file a ticket straight away about a possible defect? I am the new kid on the block here and don't know the rust process on these things.

---

<div class="post-metadata">

**Author:** ![mathstuf](https://avatars.discourse-cdn.com/v4/letter/m/958977/32.png) [@mathstuf](https://internals.rust-lang.org/u/mathstuf)\
**Post date:** [June 23, 2021, 9:40pm UTC](https://internals.rust-lang.org/t/lack-of-api-mutating-args-at-std-command/14908/17 "2021-06-23T21:40:23Z")

</div>

I think just mentioning that `Command::env` also care in that thread should be enough. Easier than splitting up the tasks associated with whatever comes out of it.

---

<div class="post-metadata">

**Author:** ![CDirkx](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cdirkx/32/8514_2.png) [@CDirkx](https://internals.rust-lang.org/u/CDirkx)\
**Post date:** [June 24, 2021, 12:54pm UTC](https://internals.rust-lang.org/t/lack-of-api-mutating-args-at-std-command/14908/18 "2021-06-24T12:54:02Z")

</div>

The `Command` `env` methods are implemented internally using the private construct `CommandEnv` ([rust/process.rs at 456a03227e3c81a51631f87ec80cac301e5fa6d7 · rust-lang/rust · GitHub](https://github.com/rust-lang/rust/blob/456a03227e3c81a51631f87ec80cac301e5fa6d7/library/std/src/sys_common/process.rs#L23-L100)), which has methods like `clear`, `set`, `remove`. An alternative would be exposing `CommandEnv` and introducing an equivalent `CommandArg` (possibly with a clearer name like `CommandEnvStore`?).

---

<div class="post-metadata">

**Author:** ![tspiteri](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/tspiteri/32/7283_2.png) [@tspiteri](https://internals.rust-lang.org/u/tspiteri)\
**Post date:** [June 24, 2021, 2:03pm UTC](https://internals.rust-lang.org/t/lack-of-api-mutating-args-at-std-command/14908/19 "2021-06-24T14:03:22Z")

</div>

For environment variables, `remove` makes sense because the variables are like a map, with key and value, and the variables do not rely on order. But arguments may depend on order: an argument can have an effect on the next arguments, so removing a single argument can be much more complicated than removing an environment variable.

In summary, while there are both `env_clear()` and `env_remove(key)`, and it looks harmless to have a similar `arg_clear()`, a method `arg_remove(index)` looks very tricky and error prone.

---

<div class="post-metadata">

**Author:** ![tspiteri](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/tspiteri/32/7283_2.png) [@tspiteri](https://internals.rust-lang.org/u/tspiteri)\
**Post date:** [June 24, 2021, 2:08pm UTC](https://internals.rust-lang.org/t/lack-of-api-mutating-args-at-std-command/14908/20 "2021-06-24T14:08:56Z")

</div>

Also, `env_clear` and `env_remove` are necessary because [`Command::new`](https://doc.rust-lang.org/std/process/struct.Command.html#method.new) inherits the current process environment, while there is no such thing for arguments. So even adding `arg_clear` is not really equivalent to `env_clear`.

[Next page](https://internals.rust-lang.org/t/lack-of-api-mutating-args-at-std-command/14908.md?page=2)
