I doubt this would get anything as short as just ~.
Maybe this could be in the prelude
use std::default::Default::default;
(once that's possible).
Then it'd be
foo(v, default(), default(), 10);
which seems tolerable.
Of course, what I'd really want there is
because once you have two "I don't care just do whatever" parameters you'll probably have more soon, so the best thing is to make it foo(v, .{ width: 10, .. }) instead of spamming in lots of ~, ~, ~, ~.
The .. grammar is fantastic for structs since the fields are generally named. However for function arguments or tuple structs, the ~ grammar can precisely specify which location to be defaulted - and functions/tuples should not have too much arguments (like > 10), so there wouldn't be too much ~ to type.
Thanks for the advise. My consideration is that functions are far more common in Rust and a person to write the code will have to ensure there is no variables d in scope, especially when it is a closure.
This is what I said "name collision" (sorry for the misspell) in the top post. Since macros are less used and has its own grammar noise !, it seems to be better.
Another option would be, regardless it is a macro or not, use a Unicode symbol to be the identifier. Would look like
#![allow(uncommon_codepoints)]
fn ࠚ<T>() -> T
where
T: Default,
{
<T as Default>::default()
}
foo(v, ࠚ(), ࠚ(), 10);
Oh, that makes sense. I hadn't thought of that; yes, macros are way less likely to get into a naming conflict. Although naturally, that would still seem like a stylistically odd/questionable choice for why to use a macro.
I read through the post and just figured out a bigger problem.
Today,
let foo = Foo { field1: bar(), ..my_default() };
hypothetically expends to (assuming my_default is generic)
let mut foo: Foo = my_default<Foo>();
foo.field1 = bar();
let foo = foo; // Remove the `mut`
This could be problematic since we might not want to write such a thing (and if my_default is Default::default() within the <Foo as Default>::default() implementation, it is infinitely recursive). We would like it to be expanded to
Small note: that's not exactly the correct desugaring. It doesn't move the whole my_default() value and then update field1; but it constructs the my_default() in a temporary variable and then creates a new instance of the struct using bar() for field1 and all the other fields are initialized by copying/moving them from the temporary.
This difference is admittedly more relevant in cases where the ..expr refers to an existing place in memory and not a newly constructed value, but even here, it better explains certain limitations such as that all fields need to be public.