Shorthand for `Default::default()`

Is there anyone proposed something similar to this?

foo(v, ~, ~, 10);

desugar to

foo(v, <_ as Default>::default(), <_ as Default>::default(),10);

Today with macros the best we can do is

foo(v, d!(), d!(), 10);

where d!() is defined as the above but since it is short it is likely to collide with definitions.

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 ~, ~, ~, ~.

5 Likes

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.

Why macros? Functions can be generic, you know?

#![feature(default_free_fn)]
use std::default::default as d;

(or define it yourself when using stable)

6 Likes

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.

There's default_free_fn - The Rust Unstable Book

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

let foo = Foo {
    field1: bar(),
    field2: my_default<_>(),
    field3: my_default<_>(),
    ...
};

So I think we might need another form of the defaulting sugar

let foo = Foo { field1: bar(), ...my_default() };

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.

4 Likes