4 ms·
One random thing that bugs me about Rust's syntax is array initialization. In Go I can initialize an array (or strictly speaking a slice) of structs like this:
by foldr 4y ago
One random thing that bugs me about Rust's syntax is array initialization. In Go I can initialize an array (or strictly speaking a slice) of structs like this:
var foo []my_struct = {{"foo", 1, "bar"}, {"foo", 1, "bar}, ...}
This is often useful in tests (where each struct value represents a test case). Rust doesn't seem to offer any similarly compact initialization syntax for arrays or Vecs. You have to write some abomination like this:
let foo = [MyStruct{a: "foo", b: 1, c: "amp"}, MyStruct{a: "bar", b: 1, c: "fff"}, MyStruct{a: "amp", b: 1, c: "aaa"} ];
Sure, it's more explicit. But even if I add a type annotation to 'foo' specifying the array type, I still have to repeat MyStruct for every member.
- guipsp 4y agoDo you mean the vec! macro?
- foldr 4y agoIt doesn't let you drop redundant struct tags, as far as I can tell.
- tialaramex 4y agoI think you're right, although mentioning a macro does suggest another option here, you could write a macro which transforms [{foo1, "bar1", baz1}, {foo2, "bar2", baz2}] into the named structure version. Since it's a macro there's no impact on runtime performance, but on the other hand it's more work to debug it. Learning declarative macros in Rust is much nicer and safer than learning C macros, but nowhere near as powerful as Rust's horribly unsafe proc macros.
- rom-antics 4y agoIf you want compact, use an array of tuples: const foos = [("foo", 1, "bar"), ("foo", 1, "bar"), ...]; for (str1, num, str2) in &foos { // ... } For a proper struct you have to name the fields, because otherwise refactoring the fields could cause struct instances to silently get out of sync with the definition.
- foldr 4y ago>otherwise refactoring the fields could cause struct instances to silently get out of sync with the definition. Having to name the fields is only part of the pain. You also have to redundantly repeat the struct name. I don't see any fundamental reason why something like this shouldn't be valid: let foo: [MyStruct; _] = [{a: "foo", b: 1, c: "amp"}, {a: "bar", b: 1, c: "fff"}, {a: "amp", b: 1, c: "aaa"} ]; I can see the logic for insisting on field names. However, the builtin 'go vet' tool has a nice behavior where it will flag the use of unkeyed literals for public structs only. This strikes me as a good compromise between concision and safety.
- tialaramex 4y agoYou can use map() to turn an array of tuples into an array of structures. Unfortunately at time of writing the optimiser doesn't do a great job on this, so if you're making an array of several thousand of something, or an array of things which are themselves very large, this might have unacceptable performance, but in cases where I have say a modest N values and I want N structures based on those values... fn make_struct(x: (&str, u8, &str)) -> MyStruct { MyStruct { a: x.0, b: x.1, c: x.2 } } let foo = [("foo", 1, "amp"), ("bar", 1, "fff"), ("amp", 1, "aaa")] .map(make_struct); [I have not actually compiled this code, but it should do roughly what you meant]
- deleted 4y ago[deleted]
- alpaca128 4y agoDropping field names from definitions like Go would make the syntax inconsistent with destructuring and pattern matching. It's always possible to define a constructor function that takes unnamed values if you care about code verbosity in test cases. Or use `type M = MyStruct` to have an abbreviated type name within the test function. Rust is more on the verbose/explicit side and I agree that can sometimes be annoying, but as autocompletion exists I can live with it.
- foldr 4y ago>Dropping field names from definitions like Go would make the syntax inconsistent with destructuring and pattern matching There's no reason you couldn't match on struct fields positionally as well. It would actually be quite convenient for cases like struct Point { x: f64, y: f64 }.
- alpaca128 4y agoIt might be convenient at first but could become a frustrating bug if you change the struct fields and also less readable in some cases. It has its upsides but I personally think making the syntax more complex for this one detail isn't worth it. Side note: matching f64 by value isn't a good idea either way, is deprecated and will become an error in the future.
- foldr 4y ago>matching f64 by value I wasn't thinking about matching floats by value, just destructuring a Point to obtain the coordinates as separate variables. Come to think of it though, as Rust already allows let Point{x, y} = pl there would be little benefit to allowing positional matching (and in fact it would create a nasty ambiguity). So yeah, as you said, you'd have to reserve the positional syntax for initialisation. My overall point here isn't that Rust has made bad design decisions. It's just that the end result of a bunch of sensible design decisions is that initialising arrays of structs is clunky. It might well be that this problem isn't fixable without creating other, worse, problems. However, I think it's a problem that's emblematic of what the OP was talking about. We have here a language that's so concerned with solving difficult problems in clever ways that it's ended up backed into a corner when it comes to something as ridiculously simple as initialising an array of structs.
- no_wizard 4y agoyou'll have to forgive me, but isn't this why there should be a `::new()` callable attached to the struct? I'm really new at Rust, but that was my takeaway, e.g. `String` has `String::new()`
- tialaramex 4y agoThe convention of providing a new() function isn't relevant here, that name isn't magic, the Rust compiler doesn't care whether it exists, and it won't cause Rust to do anything special with tuples (or any other data structure) that happen to have a similar shape. String::new() just makes you an empty String, which, since an empty String doesn't own any storage and doesn't contain anything, is very cheap (and indeed constant evaluable), likewise Vec::new() makes an empty Vec. What your parent commenter wants is for Rust to make the appropriate MyStruct, but without them needing to say MyStruct each time, which would have worked in e.g. C or Go.