It's surprisingly hard to design an argument-passing convention that can deal with variable numbers of arguments while also compiling down to a reasonable native ABI. It can be done, but the obvious implementations end up looking, at the machine-code level, a lot like "allocate a vector and then pass a pointer on the stack" or like "allocate a vector on the stack and then pass a pointer in a register."
It's also hard to design a type system that can accommodate the type Fn(U...) -> V
, and to define the affordances people expect around variadic arguments in modern languages (being able to unpack a list or a tuple into arguments, being able to iterate over arguments as a tuple or list, and so on), within the constraints of a language whose target environment is native code.
That's not to say it can't be done. It can. However, it's a ton of work, and every attempt to date has struggled with the edge cases and with getting broad adoption.
The convention in Rust, if you need something variadic-ish, is to use a vector or a similar structure. If you want the syntax to read like a function call, write a macro - that's what vec!
is, for example: a macro whose body creates a vector, then loops over the macro parameters and generating code to insert each value into the vector, then finishing the generated code with the vector itself so that the whole expression evaluates as you'd expect. Macros like println!
and format!
do something morally similar, although they rely on compiler magic to interpret the format string argument.
As for your original example, consider the following:
fn main()
{
let stuff_to_use = vec![1, 2, 1000];
let stuff_to_use: Vec<_> = stuff_to_use.into_iter()
.map(|problem| {
if problem < 100 {
2000
} else {
problem
}
})
.collect();
println!("{:?}", stuff_to_use);
}
This doesn't quite update the vector in-place the way your original code does, but it preserves a lot of the semantics while avoiding having to borrow and re-borrow the vector of initial values. If mutation in place is important, then consider the following, instead:
fn main()
{
let mut stuff_to_use = vec![1, 2, 1000];
for problem in stuff_to_use.iter_mut() {
if *problem < 100 {
*problem = 2000
}
}
println!("{:?}", stuff_to_use);
}
This does use your imperative style, but instead of trying to append the value to a new vector, it uses the &mut i32
problem
reference to modify the vector as it goes.
If the intended goal is that vec![1, 2, 1000]
becomes vec![1, 2, 1000, 2000, 2000, 1000]
, them I doubt this can be done without a temporary value to hold the new elements: you can't safely append to a vector while iterating over it, because appending might reallocate the vector, breaking the iterators. Having a vec
keep track of its iterators or store enough information that outstanding iterators can follow reallocations is probably not practical.