Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Empty struct with braces take 2 #218

Merged

Conversation

pnkfelix
Copy link
Member

When a struct type S has no fields (a so-called "empty struct"), allow it to be defined via either struct S; or struct S {}. Allow instances of struct S to be constructed and pattern-matched via either S or S {}. Allow instances of struct S {} to be constructed and pattern-matched via S {}.

Rendered

This is a semi-revival of RFC PR #147, but hopefully well-fleshed out enough for proper consideration.

@mahkoh
Copy link
Contributor

mahkoh commented Aug 29, 2014

Thanks for taking the time to do this properly.

Figure I should at least let those commenters get a bucket in the
summary, even though I personally am ambivalent at best about "+1"
comments.
@gsingh93
Copy link

My preference is still to only have one way to do it, and that would be struct S {}. The examples linked showing precedent for flexible syntax in Rust (trailing commas and redundant parenthesis) have precedent in other languages too and are very convenient. This seems different, as there are two different syntaxes that someone could reasonably assume to do different things on first glance, even though they do the same thing. I could see people using different styles of coding in their own code by forgetting to add braces occasionally, and people using different styles in large code bases that multiple people work on.

@lilyball
Copy link
Contributor

I'm still in favor of the status quo, but I am sympathetic to the macro expansion argument. However, if we're supporting struct S {} due to macro expansion, it seems inconsistent to not support struct S() as well. You already covered why that's a problem, though, and to me that's a point in favor of keeping the status quo instead.

@SiegeLord
Copy link

+1 to "Always Require Braces".

Alternatively, (not sure if this was proposed in the RFC), treat struct Foo; and struct Foo{} as different types, with one requiring you to omit braces during construction and another to force them.

@pnkfelix
Copy link
Member Author

@SiegeLord hmm, I guess I regarded that option as a particular variant of "Always Require Braces", since you can then encode struct S0 as enum S0 { S0 } (as mentioned in the "Always Require Braces" description.

But I guess I should put it in as an explicit sub-variation, in terms of continuing to support the notation struct S0;.

@liigo
Copy link
Contributor

liigo commented Sep 13, 2014

+1 to "Always Require Braces". Try to "do a thing in one way".
struct S; is weird, like C' pre-declaration, not struct definition.

@CloudiDust
Copy link
Contributor

I think we should either maintain the status quo, or support all three ways to do this thing:

struct S;/struct S {}/struct S ();.

Because choosing either struct S {} and struct S (); would rise the question "why not also the other?"

And if we are to support those two, why don't we continue to support struct S;?

Also, if we support both struct S {} and struct S ();, which one should we prefer?

So I think we should still prefer to use struct S;, but leave the other two for using in macros.

@CloudiDust
Copy link
Contributor

@liigo, structs already support doing one thing in two ways: you can choose whether to leave out the last comma or not.

struct S { a: i32, b: i32, } and struct S { a: i32, b: i32 } are both legal.

So I think having three ways to declare an empty struct is okay.

@nikomatsakis
Copy link
Contributor

cc me

@liigo
Copy link
Contributor

liigo commented Sep 25, 2014

I think we should either maintain the status quo, or support all three ways to do this thing:
struct S; / struct S {} / struct S ();

I don't think it's a good smell supporting so many ways to do a very simple thing.

@pnkfelix
Copy link
Member Author

@CloudiDust I cannot tell from your comment; are you overlooking the detail that the RFC explicitly explains why supporting struct S (); as well is not a good idea, in the section Empty Tuple Structs ?

@CloudiDust
Copy link
Contributor

@pnkfelix Eh, yes, I think I was overlooking it. Sorry.

@liigo I was wrong and the third way is invalid. In this case, I think we can have two ways to do it, but still prefer struct S; in handwritten code.

@pnkfelix
Copy link
Member Author

pnkfelix commented Oct 7, 2014

At a recent meeting it was pointed out that the approach of this RFC is a little too simplistic, in that it would break the current property that when you write struct S { ... }, you only get a binding in the type namespace, not the value namespace. (Compare to struct S;, which puts a binding into both the type and value namespaces.)

So I plan to address this by modifying this proposal slightly: instead of treating struct S; and struct S { } as synonymous, change things so that struct S { } only adds an entry to the type namespace (and thus you would have to write S { } to construct an instance of that struct), while struct S would, as today, add entries to both the type and value namespaces, so that you can write either S or S { } to construct an instance of such a struct.

(Arguably the latter proposal is sufficiently complicated as to push me further toward the "Always Require Braces" camp. But I have not landed there yet.)

@brson
Copy link
Contributor

brson commented Feb 26, 2015

Is there a technical reason that flexible structs can't be constructed and matched with braces? With that restriction the 'flexiness' of the struct matters to the caller, though it otherwise seems irrelevant.

On cursory thought my preference is to make the non-brace form a special allowance for declaration, construction and matching for any struct that doesn't have fields.

@vadimcn
Copy link
Contributor

vadimcn commented Feb 26, 2015

👍 for allowing S {} and S ().

If struct S; can be supported without too much headache, sure, let's do it, but it isn't really that important, IMO.

On the other hand, I've always found it jarring having to put braces after impls of marker traits. I'd much rather like to see impl Copy for Foo; than impl Copy for Foo {}.

@nikomatsakis
Copy link
Contributor

I'm 👍 on the RFC as written, I think. I like the idea that the canonical way to declare a struct is with braces, but we offer shorthands analogous to those available to enum variants. In this way, struct S; can be seen as shorthand for:

struct S { }
const S = S { };

Similarly, struct S(T) can be seen as shorthand for:

struct S { 0: T }
const fn S(t: T) { S { 0: T } }

With the caveat that you can't declare fields with numeric names and that we haven't accepted #911, of course.

Still, that makes sense to me (and the same, incidentally, applies to enum variants).

@eddyb
Copy link
Member

eddyb commented Feb 27, 2015

@nikomatsakis If we had adopted a different struct literal style, your second example would look better:

const fn S(t: T) -> S { S { .0 = t } }

@nikomatsakis
Copy link
Contributor

@brson

Is there a technical reason that flexible structs can't be constructed and matched with braces? With that restriction the 'flexiness' of the struct matters to the caller, though it otherwise seems irrelevant.

I don't understand this question... as I read it, the RFC seemed to say that a flexible struct could be constructed and matched with braces (or without):

Both braced and flexible empty structs can be constructed via the expression syntax S { } ("new")...Both braced and flexible empty structs can be pattern-matched via the pattern syntax S { } ("new").

@nikomatsakis
Copy link
Contributor

@eddyb (I don't personally think that looks better, but it's neither here nor there.)

@eddyb
Copy link
Member

eddyb commented Feb 27, 2015

@nikomatsakis I should have phrased that as "it would look more like a natural feature".

@codyps
Copy link

codyps commented Feb 27, 2015

I've run into the similar problem to this with enum variants when trying to wrap & generate enums within a macro:

enum F {
    X,
    Y(u32),
}

// X() is forbidden

My conclusion after a bit of macro contortion is that due to this syntax requirement, macro_rules! can't presently wrap enums that include no-arg variants :(

I expect macros-wrapping struct definitions run into the same problem, but I haven't looked down that path yet.

@eddyb
Copy link
Member

eddyb commented Feb 28, 2015

@jmesmon I've done it a few times in the past, but it complicates the macro significantly (sometimes the end result is so bad it's just not worth it).
It would be interesting to analyze your attempt, if you still have any of it saved somewhere.

@nikomatsakis
Copy link
Contributor

@pnkfelix something I was just wondering. Today, tuple-structs can't be constructed using brace syntax, so is there a reason to have "flexible" structs support both options? It seems mildly inconsistent? Or am I confused?

(Personally though I think I'd rather that all structs can be built using braces, just using 0: as the field names, because that seems to support my "sugar for..." outlook.)

@brson
Copy link
Contributor

brson commented Mar 11, 2015

I don't understand this question... as I read it, the RFC seemed to say that a flexible struct could be constructed and matched with braces (or without):

I think I stated my concern backwards: this is from the RFC "Both braced and flexible empty structs can be constructed via the expression syntax S { } ("new"). Flexible empty structs, as today, can also be constructed via the expression syntax S."

It seems to indicate that braced structs can't be constructed with the non-braced syntax - the case it doesn't mention is 'braced structs can be constructed via the expression syntax S".

@pnkfelix
Copy link
Member Author

@brson okay. that's right, braced structs deliberately cannot be constructed via non-brace syntax, because braced structs do not have an entry in the value namespace, only the type namespace.

@pnkfelix
Copy link
Member Author

@nikomatsakis If I understand you correctly, It sounds like you are asking "why did the RFC take this route, and not go down the route of the 'Never synonymous' option?"

is that right? I don't really have more to add on the topic than what I wrote there: its hard to justify having both syntaxes available if you're not actually going to be flexible about their usage.


Or maybe you are semi-implicitly asking for a new alternative to be added to the RFC that attempts to pull tuple-structs into the mix? I am not personally opposed to a TupleStruct { 0: ..., 1: ... } syntax, but I also think that can and should be addressed orthogonally to this RFC...

(I argue its orthogonal because, to my knowledge, there is no empty tuple struct, but all of the structs addressed by this RFC are empty.)

@nikomatsakis nikomatsakis assigned nikomatsakis and unassigned pnkfelix Apr 9, 2015
@nikomatsakis
Copy link
Contributor

@pnkfelix never mind, having given this more thought, I think that your specification is compatible with what I want -- that is, struct Foo; is sugar for struct Foo { } const Foo = Foo { };. It does not go as far as I might (eventually) like, where struct Foo(i32) is sugar for struct Foo { 0: i32 } const fn Foo(x: i32) -> Foo { Foo { 0: x } }, but I agree that's a separate RFC. And anyway even without making it literal sugar, I feel like everything is conceptually sugar, it's just that 0 is not a legal field name.

@nikomatsakis
Copy link
Contributor

RFC 218 is accepted. Tracking issue: rust-lang/rust#24266

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-data-types RFCs about data-types A-expressions Term language related proposals & ideas A-patterns Pattern matching related proposals & ideas A-product-types Product type related proposals A-syntax Syntax related proposals & ideas
Projects
None yet
Development

Successfully merging this pull request may close these issues.