blob: f1d057499a9ec5b9458e76c003f60a229f1e22aa [file] [log] [blame]
Russ Coxaf5018f2020-03-09 23:54:35 -04001# Go's Declaration Syntax
Andrew Gerranddb9a09f2013-03-08 11:17:09 +110027 Jul 2010
Andrew Gerrandb316fcd2013-06-05 09:59:16 +10003Tags: c, syntax, ethos
Russ Coxfaf1e2d2020-03-14 09:44:01 -04004Summary: Why Go's declaration syntax doesn't look like, and is much simpler than, C's.
Russ Cox972d42d2020-03-15 15:50:36 -04005OldURL: /gos-declaration-syntax
Andrew Gerranddb9a09f2013-03-08 11:17:09 +11006
7Rob Pike
8
Russ Coxaf5018f2020-03-09 23:54:35 -04009## Introduction
Andrew Gerranddb9a09f2013-03-08 11:17:09 +110010
Russ Cox482079d2020-03-09 22:11:04 -040011Newcomers to Go wonder why the declaration syntax is different from the
12tradition established in the C family.
13In this post we'll compare the two approaches and explain why Go's declarations look as they do.
Andrew Gerranddb9a09f2013-03-08 11:17:09 +110014
Russ Coxaf5018f2020-03-09 23:54:35 -040015## C syntax
Andrew Gerranddb9a09f2013-03-08 11:17:09 +110016
Russ Cox482079d2020-03-09 22:11:04 -040017First, let's talk about C syntax. C took an unusual and clever approach
18to declaration syntax.
19Instead of describing the types with special syntax,
20one writes an expression involving the item being declared,
21and states what type that expression will have. Thus
Andrew Gerranddb9a09f2013-03-08 11:17:09 +110022
23 int x;
24
Russ Cox482079d2020-03-09 22:11:04 -040025declares x to be an int: the expression 'x' will have type int.
26In general, to figure out how to write the type of a new variable,
27write an expression involving that variable that evaluates to a basic type,
28then put the basic type on the left and the expression on the right.
Andrew Gerranddb9a09f2013-03-08 11:17:09 +110029
30Thus, the declarations
31
32 int *p;
33 int a[3];
34
Russ Coxaf5018f2020-03-09 23:54:35 -040035state that p is a pointer to int because '\*p' has type int,
Russ Cox482079d2020-03-09 22:11:04 -040036and that a is an array of ints because a[3] (ignoring the particular index value,
37which is punned to be the size of the array) has type int.
Andrew Gerranddb9a09f2013-03-08 11:17:09 +110038
Russ Cox482079d2020-03-09 22:11:04 -040039What about functions? Originally, C's function declarations wrote the types
40of the arguments outside the parens, like this:
Andrew Gerranddb9a09f2013-03-08 11:17:09 +110041
42 int main(argc, argv)
43 int argc;
44 char *argv[];
45 { /* ... */ }
46
Russ Cox482079d2020-03-09 22:11:04 -040047Again, we see that main is a function because the expression main(argc,
48argv) returns an int.
49In modern notation we'd write
Andrew Gerranddb9a09f2013-03-08 11:17:09 +110050
51 int main(int argc, char *argv[]) { /* ... */ }
52
53but the basic structure is the same.
54
Russ Cox482079d2020-03-09 22:11:04 -040055This is a clever syntactic idea that works well for simple types but can get confusing fast.
56The famous example is declaring a function pointer.
57Follow the rules and you get this:
Andrew Gerranddb9a09f2013-03-08 11:17:09 +110058
59 int (*fp)(int a, int b);
60
Russ Coxaf5018f2020-03-09 23:54:35 -040061Here, fp is a pointer to a function because if you write the expression (\*fp)(a,
Russ Cox482079d2020-03-09 22:11:04 -040062b) you'll call a function that returns int.
63What if one of fp's arguments is itself a function?
Andrew Gerranddb9a09f2013-03-08 11:17:09 +110064
65 int (*fp)(int (*ff)(int x, int y), int b)
66
67That's starting to get hard to read.
68
69Of course, we can leave out the name of the parameters when we declare a function, so main can be declared
70
71 int main(int, char *[])
72
73Recall that argv is declared like this,
74
75 char *argv[]
76
Russ Cox482079d2020-03-09 22:11:04 -040077so you drop the name from the middle of its declaration to construct its type.
Russ Coxaf5018f2020-03-09 23:54:35 -040078It's not obvious, though, that you declare something of type char \*[] by
Russ Cox482079d2020-03-09 22:11:04 -040079putting its name in the middle.
Andrew Gerranddb9a09f2013-03-08 11:17:09 +110080
81And look what happens to fp's declaration if you don't name the parameters:
82
83 int (*fp)(int (*)(int, int), int)
84
85Not only is it not obvious where to put the name inside
86
87 int (*)(int, int)
88
Russ Cox482079d2020-03-09 22:11:04 -040089it's not exactly clear that it's a function pointer declaration at all.
90And what if the return type is a function pointer?
Andrew Gerranddb9a09f2013-03-08 11:17:09 +110091
92 int (*(*fp)(int (*)(int, int), int))(int, int)
93
94It's hard even to see that this declaration is about fp.
95
Russ Cox482079d2020-03-09 22:11:04 -040096You can construct more elaborate examples but these should illustrate some
97of the difficulties that C's declaration syntax can introduce.
Andrew Gerranddb9a09f2013-03-08 11:17:09 +110098
Russ Cox482079d2020-03-09 22:11:04 -040099There's one more point that needs to be made, though.
100Because type and declaration syntax are the same,
101it can be difficult to parse expressions with types in the middle.
102This is why, for instance, C casts always parenthesize the type, as in
Andrew Gerranddb9a09f2013-03-08 11:17:09 +1100103
104 (int)M_PI
105
Russ Coxaf5018f2020-03-09 23:54:35 -0400106## Go syntax
Andrew Gerranddb9a09f2013-03-08 11:17:09 +1100107
Russ Cox482079d2020-03-09 22:11:04 -0400108Languages outside the C family usually use a distinct type syntax in declarations.
109Although it's a separate point, the name usually comes first,
110often followed by a colon.
111Thus our examples above become something like (in a fictional but illustrative language)
Andrew Gerranddb9a09f2013-03-08 11:17:09 +1100112
113 x: int
114 p: pointer to int
115 a: array[3] of int
116
Russ Cox482079d2020-03-09 22:11:04 -0400117These declarations are clear, if verbose - you just read them left to right.
118Go takes its cue from here, but in the interests of brevity it drops the
119colon and removes some of the keywords:
Andrew Gerranddb9a09f2013-03-08 11:17:09 +1100120
121 x int
122 p *int
123 a [3]int
124
Russ Cox482079d2020-03-09 22:11:04 -0400125There is no direct correspondence between the look of [3]int and how to
126use a in an expression.
127(We'll come back to pointers in the next section.) You gain clarity at the
128cost of a separate syntax.
Andrew Gerranddb9a09f2013-03-08 11:17:09 +1100129
Russ Cox482079d2020-03-09 22:11:04 -0400130Now consider functions. Let's transcribe the declaration for main as it would read in Go,
131although the real main function in Go takes no arguments:
Andrew Gerranddb9a09f2013-03-08 11:17:09 +1100132
Rob Pikef7678ed2014-05-11 06:59:47 -0400133 func main(argc int, argv []string) int
Andrew Gerranddb9a09f2013-03-08 11:17:09 +1100134
Russ Cox482079d2020-03-09 22:11:04 -0400135Superficially that's not much different from C,
136other than the change from `char` arrays to strings,
137but it reads well from left to right:
Andrew Gerranddb9a09f2013-03-08 11:17:09 +1100138
Rob Pikef7678ed2014-05-11 06:59:47 -0400139function main takes an int and a slice of strings and returns an int.
Andrew Gerranddb9a09f2013-03-08 11:17:09 +1100140
141Drop the parameter names and it's just as clear - they're always first so there's no confusion.
142
Rob Pikef7678ed2014-05-11 06:59:47 -0400143 func main(int, []string) int
Andrew Gerranddb9a09f2013-03-08 11:17:09 +1100144
Russ Cox482079d2020-03-09 22:11:04 -0400145One merit of this left-to-right style is how well it works as the types
146become more complex.
147Here's a declaration of a function variable (analogous to a function pointer in C):
Andrew Gerranddb9a09f2013-03-08 11:17:09 +1100148
149 f func(func(int,int) int, int) int
150
151Or if f returns a function:
152
153 f func(func(int,int) int, int) func(int, int) int
154
Russ Cox482079d2020-03-09 22:11:04 -0400155It still reads clearly, from left to right,
156and it's always obvious which name is being declared - the name comes first.
Andrew Gerranddb9a09f2013-03-08 11:17:09 +1100157
158The distinction between type and expression syntax makes it easy to write and invoke closures in Go:
159
160 sum := func(a, b int) int { return a+b } (3, 4)
161
Russ Coxaf5018f2020-03-09 23:54:35 -0400162## Pointers
Andrew Gerranddb9a09f2013-03-08 11:17:09 +1100163
Russ Cox482079d2020-03-09 22:11:04 -0400164Pointers are the exception that proves the rule.
165Notice that in arrays and slices, for instance,
166Go's type syntax puts the brackets on the left of the type but the expression
167syntax puts them on the right of the expression:
Andrew Gerranddb9a09f2013-03-08 11:17:09 +1100168
169 var a []int
170 x = a[1]
171
Russ Coxaf5018f2020-03-09 23:54:35 -0400172For familiarity, Go's pointers use the \* notation from C,
Russ Cox482079d2020-03-09 22:11:04 -0400173but we could not bring ourselves to make a similar reversal for pointer types.
174Thus pointers work like this
Andrew Gerranddb9a09f2013-03-08 11:17:09 +1100175
176 var p *int
177 x = *p
178
179We couldn't say
180
181 var p *int
182 x = p*
183
Russ Coxaf5018f2020-03-09 23:54:35 -0400184because that postfix \* would conflate with multiplication. We could have used the Pascal ^, for example:
Andrew Gerranddb9a09f2013-03-08 11:17:09 +1100185
186 var p ^int
187 x = p^
188
Russ Cox482079d2020-03-09 22:11:04 -0400189and perhaps we should have (and chosen another operator for xor),
190because the prefix asterisk on both types and expressions complicates things
191in a number of ways.
192For instance, although one can write
Andrew Gerranddb9a09f2013-03-08 11:17:09 +1100193
194 []int("hi")
195
Russ Coxaf5018f2020-03-09 23:54:35 -0400196as a conversion, one must parenthesize the type if it starts with a \*:
Andrew Gerranddb9a09f2013-03-08 11:17:09 +1100197
198 (*int)(nil)
199
Russ Coxaf5018f2020-03-09 23:54:35 -0400200Had we been willing to give up \* as pointer syntax, those parentheses would be unnecessary.
Andrew Gerranddb9a09f2013-03-08 11:17:09 +1100201
Russ Cox482079d2020-03-09 22:11:04 -0400202So Go's pointer syntax is tied to the familiar C form,
203but those ties mean that we cannot break completely from using parentheses
204to disambiguate types and expressions in the grammar.
Andrew Gerranddb9a09f2013-03-08 11:17:09 +1100205
206Overall, though, we believe Go's type syntax is easier to understand than C's, especially when things get complicated.
207
Russ Coxaf5018f2020-03-09 23:54:35 -0400208## Notes
Andrew Gerranddb9a09f2013-03-08 11:17:09 +1100209
Russ Cox482079d2020-03-09 22:11:04 -0400210Go's declarations read left to right. It's been pointed out that C's read in a spiral!
Russ Coxaf5018f2020-03-09 23:54:35 -0400211See [ The "Clockwise/Spiral Rule"](http://c-faq.com/decl/spiral.anderson.html) by David Anderson.