-
Notifications
You must be signed in to change notification settings - Fork 368
Description
(I couldn't find any previous discussion on this. If I missed something, please let me know.)
Problem
To initialize a variable of a structure type, there are mainly two options:
- Use the structure built-in value constructor, e.g.
MyStruct(1, 2, 3) - Zero initialize the variable and then assign each field, e.g.
var s = MyStruct(); s.foo = 1; s.bar = 2; s.baz = 3;
Both of these have disadvantages:
In the constructor function, the field names are not used, which can lead to various problems. Callers might switch up arguments, especially if the arguments are of the same type. If the order of struct fields is ever changed, one might forget to update these initialization calls. Also, the initialization is a lot harder to read in many cases: for example with Material(vec3(1.0, 0.0, 0.0), 0.5, 0.2) you can just about understand the first argument, but without actively thinking about the order of struct fields, you have no idea what the other values mean. Metallic? Roughness? ... and in what order?
The field-access assignment solution is sub-optimal because it requires var, even if the value is not changed after the initialization. It also inconvenient because if you just want to pass a structure to a function, you have to introduce a local variable. Finally, when fields are added to the structure later on, it is easy to forget updating all initialization spots as no compiler will warn about this.
Suggestion
Add a struct initialization expression, like in Rust (with the basic field: value, syntax being used in many languages, including Javascript):
let s = MyStruct {
foo: 1,
bar: 2,
baz: 3,
};
It is an error if this expression is missing fields, i.e. all fields must be explicitly initialized. This new syntax would solve all problems of the existing solutions mentioned above. In particular, this makes it a lot more convenient to pass structures to functions:
pbr(lightColor, lightDir, Material {
baseColor: vertex.color,
roughness: vertex.roughness,
metallic: 0,
});
Possible extensions
The above syntax would already help a lot, but it might be worth adding two further features, also taken from Rust:
..fill syntax
Once as a last thing inside the {} braces, one can write ..<expr> where <expr> has the structure type. All fields that have not been initialized explicitly before are filled from <expr>. Example:
let s = MyStruct {
foo: 27,
bar: 8,
..MyStruct()
};
let t = MyStruct {
bar: 99,
..s
};
s would be MyStruct(27, 8, 0) and t would be MyStruct(27, 99, 0).
Alternatively, one could go with the JS approach of allowing multiple ... entries in the braces, intermixed with single field initializations, with later entries overriding earlier ones.
foo shortcut for foo: foo
Let <ident> in the initializer list be equivalent to <ident>: <ident>, to make initializing fields from variables with the same name easier. Example:
let foo = 27;
let s = MyStruct {
foo, // same as `foo: foo`
bar: 2,
baz: 3,
};