Pre-RFC: Allow 0,1,2,... as "identifier" in structs

Not sure this even needs a RFC, but pre-RFC was the most fitting title.

I would be happy to work on the implementation - if this is deemed a wanted change.

I am not sure I understand the reasoning why this is not allowed:

struct Foo {
    0: i32, // This is the only thing that fails ...
    arg_1: i32,
    named_1: i32,
}

fn main() {
    let foo = Foo { 0: 0, arg_1: 1, named_1: 42} ;
    println!("{}", foo.0);
}

fails only at the struct definition with "^ expected identifier". Would it not just be necessary to change 'IDENTIFIER' to 'IDENTIFIER_OR_NUMBER' (pseudo-code wise)?

to make it all work as everything else seems to be able to work with '.0', '0: ', etc.?

I am also not seeing the drawback given that it is already inconsistent right now.

My personal motivation with that is to mirror named function arguments in structs as they then perfectly match the:

0: arg_0, 1: arg_1, named_1: arg_3

pattern. Also getting this change (0: as allowed identifier in structural records) in independent of structural records is one less thing that structural records need to handle - and it is planned as part of their RFC. (https://github.com/rust-lang/rfcs/pull/2584/files/051686558a1b57448193fbc2938f1a3f193e9c7e#diff-82f56a33e385fa4523376256280d53cfR635-R646)

To be precise: I am only suggesting to allow numbers as identifiers, nothing else - so there is still plenty of distinction from Tuple to Struct, but they can be 1:1 mapped, but as for every other identifier you need to be explicit about the 0, 1, ...:

Tuple (i32, i32) ~= Struct { 0: i32, 1: i32 }

[They are not equal, just mappable]

If you haven't seen it already, for this part you'd probably be interested in https://github.com/rust-lang/rfcs/blob/master/text/1506-adt-kinds.md#tuple-structs

Tuple structs really are "just" structs with integer members; this works:

#[derive(Debug)]
struct Tuple(usize, usize);

fn main() {
    let tuple = Tuple { 0: 0, 1: 1 };
    let Tuple { 0: _, 1: _ } = tuple;
    dbg!(tuple);
}

However, I fail to see the concrete benefit to allowing mixing numbered fields and named fields. To be completely honest, were Rust to not have tuple structs (nominal product types with indexed fields) yet (but ignoring their use in enums), I could definitely see them not being motivated enough to add to the languagenote 1.

This is a lot of rambling to basically say: you need more/better motivation. You can make the argument for consistency since the "numbered fields" syntax is allowed for accessing fields and braced patterns for tuple structs, but tuple structs are already special (the function call pattern) so it's not like this would make tuple structs just sugar over braced structs.

This could potentially be argued as a reasonable extension to the language, but there needs to be some reasonable effort put into motivation beyond just the consistency angle, imho. In addition, questions like what integer literals are allowed (do they have to be continuous, start at zero, can they come after named fields, etc) have to be answered.

Also,

is probably not what you're actually suggesting; that'd mean allowing e.g. let 0 = "Hello, world!"; print(0); with 0 as an identifier meaning let zero = "Hello, world!"; print(zero); You don't want integer literals ad identifers, but to allow integer literals as struct field names (in more places).

note 1 again, ignoring enums. Enums are weird conglomerates of both (type theoretical) sum types and product types, and if we only had the braced variant enum E { Variant { name: Data } } form, I can definitely see quickly moving to add in the tuple variants there, and once tuple variants exist, allowing them as full tuple structs instead as well as variants. But perhaps this magical theoretical Rust without tuple structs has "purer" sum type enumerations, where each variant is a tag associated to some existing type, rather than being its own product type?

9 Likes

I agree that the extension proposed here is not needed, but just about this point, tuple types are invaluable for returning multiple values at once. This is the main use of them in Python, for example.

I'm not talking about regular structural tuples (f32, f32), but noninal tuple structs struct f32x2(f32, f32);.

I'm in full agreement that structural tuples definitely pull their weight. Even in languages without first-class structural types, tuples get reimplemented as Pair<A, B> and so on.

It's the nominal type with indexed members that I don't see pulling weight in a vacuum.

struct Foo(u32, u32);

// is roughly equivalent to (modulo patterns)

struct Foo {
    0: u32,
    1: u32,
}
fn Foo(_0: u32, _1: u32) -> Foo {
    Foo { 0: _0, 1: _1 }
}

And I claim that tuple structs would be hard-fought to motivate coming off of failing to meaningfully motivate the exact same thing (replacing named members with indexed members) for enum.

Oh, sorry. If so, I completely agree. That encourage you to be lazy and not write meaningful names... If you use your struct as a variable and not deconstruct it immediately, it's probably a problem.

The main value of tuple structures is to support the newtype pattern - they allow Rust to have it without needing a dedicated language feature. But if Rust had a dedicated newtype and no tuple structs then I don't think you'd add them.

(I don't know if you intended to hide this behind a macro, but if you did, _0, _1, etc. are valid identifiers).

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.