###Summary
Rust already has these kinds of struct
s:
struct Rgb (i8, i8, i8)
the unnamed analog is
(25i8, 25i8, 80i8)
I am proposing that this is extended to make this
struct Rgb { red: i8, green: i8, blue: i8 }
have this unnamed analog
{ red: 25i8, green: 25i8, blue: 80i8 }
in other words, tuples : tuple structs :: unnamed structs : structs
###Motivation Since tuples exist, they give a way to have duck-typed unnamed types. Tuples are defined by argument order. Structs are not. This is perfectly valid:
let color = Rgb { green: 25i8, red: 25i8, blue: 80i8 };
By using struct
s, you prevent errors from wrong ordering of arguments. You can get more lightweight syntax by using tuple struct
s in cases where your types are different since the wrong ordering is detected by the compiler. Even more lightweight are duck-typed tuples (unnamed) because they don’t need to be declared. However, Rust lacks an anonymous struct
type that can be used in a duck-typed manner.
This encourages API writers to either use tuples/arrays despite the ordering hazard or to just have two arguments that are conceptually part of the same anonymous struct
to be passed as two separate values.
Here’s a line from http://www.piston.rs/
...
rectangle([1.0, 0.0, 0.0, 1.0], // red
[0.0, 0.0, 100.0, 100.0],
c.transform, g);
This is the kind of API design that happens when you can’t express something like
rectangle({red: 1.0, blue: 0.0, green: 1.0, alpha: 1.0},
{position: {x: 0.0, y: 0.0}, size: {width: 100.0, height: 100.0}},
c.transform, g);
in a very simple way without having to declare multiple types. In JavaScript, these kinds of structured data patterns are very common because of how easy they are to write. In fact, JSON is a very popular data exchange format, and the proposed Rust syntax mimics it closely.
The function would be declared similarly as:
fn rectangle(color: {red: f32, blue: f32, green: f32, alpha: f32},
pose: {position: {x: f32, y: f32}, size: {width: f32, height: f32}}, ... )
Inside the function you can refer to these as color.red
and pose.position.x
as you’d expect.
This also attacks the issue of named arguments in an orthogonal way:
window.addNewControl("Title", 20, 50, 100, 50, true);
the API author now has the choice to write this call as
window.addNewControl({title: "Title",
x: 20,
y: 50,
width: 100,
height: 50,
drawingNow: true});
Again, for an example of this refer to the JavaScript community. Most libraries have agreed to write their APIs this way.
Drawbacks
This has the same drawbacks in terms of traits as tuples do. That means since this is allowed:
impl Trait for (i8,)
then you’d have to allow this:
impl Trait for {foo: i8,}
There’s also a syntactic ambiguity:
let a = {b: c}; //what does this mean?
If type ascription is added, it could mean a block with the value b
and of type c
or it could mean an unnamed struct
. So possibly one value unnamed struct
s must be used as following:
let a = {b: c,}; //unnamed struct
let a = {b: c: D,}; //unnamed struct with type ascription
###Alternatives
Add full keyword arguments, but not unnamed struct
s. This way the feature doesn’t leak into block syntax/type ascription, only affecting API design. This feels more “special cased” and goes against solving things in an orthogonal way. However, this kind of a design might solve optional and default parameters more easily without having to rely on some kind of strange overloading since I have no design for optional/default parameters (how do you deal with a struct that doesn’t have the fields you want?)