mirror of
https://github.com/golang/go
synced 2024-11-21 11:44:43 -07:00
doc: add Go's declaration syntax article
Originally published on The Go Programming Language Blog, July 7, 2010. I did not put any go file in doc/progs. Shoul I include Go files with those declarations? http://blog.golang.org/2010/07/gos-declaration-syntax.html Update #2547. R=golang-dev, adg CC=golang-dev https://golang.org/cl/5795068
This commit is contained in:
parent
82fc28c0f5
commit
6033a48b77
348
doc/articles/gos_declaration_syntax.html
Normal file
348
doc/articles/gos_declaration_syntax.html
Normal file
@ -0,0 +1,348 @@
|
||||
<!--{
|
||||
"Title": "Go's Declaration Syntax"
|
||||
}-->
|
||||
|
||||
<p>
|
||||
Newcomers to Go wonder why the declaration syntax is different from the
|
||||
tradition established in the C family. In this post we'll compare the
|
||||
two approaches and explain why Go's declarations look as they do.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>C syntax</b>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
First, let's talk about C syntax. C took an unusual and clever approach
|
||||
to declaration syntax. Instead of describing the types with special
|
||||
syntax, one writes an expression involving the item being declared, and
|
||||
states what type that expression will have. Thus
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
int x;
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
declares x to be an int: the expression 'x' will have type int. In
|
||||
general, to figure out how to write the type of a new variable, write an
|
||||
expression involving that variable that evaluates to a basic type, then
|
||||
put the basic type on the left and the expression on the right.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Thus, the declarations
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
int *p;
|
||||
int a[3];
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
state that p is a pointer to int because '*p' has type int, and that a
|
||||
is an array of ints because a[3] (ignoring the particular index value,
|
||||
which is punned to be the size of the array) has type int.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
What about functions? Originally, C's function declarations wrote the
|
||||
types of the arguments outside the parens, like this:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
int main(argc, argv)
|
||||
int argc;
|
||||
char *argv[];
|
||||
{ /* ... */ }
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Again, we see that main is a function because the expression main(argc,
|
||||
argv) returns an int. In modern notation we'd write
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
int main(int argc, char *argv[]) { /* ... */ }
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
but the basic structure is the same.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
This is a clever syntactic idea that works well for simple types but can
|
||||
get confusing fast. The famous example is declaring a function pointer.
|
||||
Follow the rules and you get this:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
int (*fp)(int a, int b);
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Here, fp is a pointer to a function because if you write the expression
|
||||
(*fp)(a, b) you'll call a function that returns int. What if one of fp's
|
||||
arguments is itself a function?
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
int (*fp)(int (*ff)(int x, int y), int b)
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
That's starting to get hard to read.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Of course, we can leave out the name of the parameters when we declare a
|
||||
function, so main can be declared
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
int main(int, char *[])
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Recall that argv is declared like this,
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
char *argv[]
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
so you drop the name from the <em>middle</em> of its declaration to construct
|
||||
its type. It's not obvious, though, that you declare something of type
|
||||
char *[] by putting its name in the middle.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
And look what happens to fp's declaration if you don't name the
|
||||
parameters:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
int (*fp)(int (*)(int, int), int)
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Not only is it not obvious where to put the name inside
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
int (*)(int, int)
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
it's not exactly clear that it's a function pointer declaration at all.
|
||||
And what if the return type is a function pointer?
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
int (*(*fp)(int (*)(int, int), int))(int, int)
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
It's hard even to see that this declaration is about fp.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
You can construct more elaborate examples but these should illustrate
|
||||
some of the difficulties that C's declaration syntax can introduce.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
There's one more point that needs to be made, though. Because type and
|
||||
declaration syntax are the same, it can be difficult to parse
|
||||
expressions with types in the middle. This is why, for instance, C casts
|
||||
always parenthesize the type, as in
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
(int)M_PI
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
<b>Go syntax</b>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Languages outside the C family usually use a distinct type syntax in
|
||||
declarations. Although it's a separate point, the name usually comes
|
||||
first, often followed by a colon. Thus our examples above become
|
||||
something like (in a fictional but illustrative language)
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
x: int
|
||||
p: pointer to int
|
||||
a: array[3] of int
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
These declarations are clear, if verbose - you just read them left to
|
||||
right. Go takes its cue from here, but in the interests of brevity it
|
||||
drops the colon and removes some of the keywords:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
x int
|
||||
p *int
|
||||
a [3]int
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
There is no direct correspondence between the look of [3]int and how to
|
||||
use a in an expression. (We'll come back to pointers in the next
|
||||
section.) You gain clarity at the cost of a separate syntax.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Now consider functions. Let's transcribe the declaration for main, even
|
||||
though the main function in Go takes no arguments:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
func main(argc int, argv *[]byte) int
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Superficially that's not much different from C, but it reads well from
|
||||
left to right:
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<em>function main takes an int and a pointer to a slice of bytes and returns an int.</em>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Drop the parameter names and it's just as clear - they're always first
|
||||
so there's no confusion.
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
func main(int, *[]byte) int
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
One value of this left-to-right style is how well it works as the types
|
||||
become more complex. Here's a declaration of a function variable
|
||||
(analogous to a function pointer in C):
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
f func(func(int,int) int, int) int
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Or if f returns a function:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
f func(func(int,int) int, int) func(int, int) int
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
It still reads clearly, from left to right, and it's always obvious
|
||||
which name is being declared - the name comes first.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The distinction between type and expression syntax makes it easy to
|
||||
write and invoke closures in Go:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
sum := func(a, b int) int { return a+b } (3, 4)
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
<b>Pointers</b>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Pointers are the exception that proves the rule. Notice that in arrays
|
||||
and slices, for instance, Go's type syntax puts the brackets on the left
|
||||
of the type but the expression syntax puts them on the right of the
|
||||
expression:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
var a []int
|
||||
x = a[1]
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
For familiarity, Go's pointers use the * notation from C, but we could
|
||||
not bring ourselves to make a similar reversal for pointer types. Thus
|
||||
pointers work like this
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
var p *int
|
||||
x = *p
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
We couldn't say
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
var p *int
|
||||
x = p*
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
because that postfix * would conflate with multiplication. We could have
|
||||
used the Pascal ^, for example:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
var p ^int
|
||||
x = p^
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
and perhaps we should have (and chosen another operator for xor),
|
||||
because the prefix asterisk on both types and expressions complicates
|
||||
things in a number of ways. For instance, although one can write
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
[]int("hi")
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
as a conversion, one must parenthesize the type if it starts with a *:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
(*int)(nil)
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Had we been willing to give up * as pointer syntax, those parentheses
|
||||
would be unnecessary.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
So Go's pointer syntax is tied to the familiar C form, but those ties
|
||||
mean that we cannot break completely from using parentheses to
|
||||
disambiguate types and expressions in the grammar.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Overall, though, we believe Go's type syntax is easier to understand
|
||||
than C's, especially when things get complicated.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>Notes</b>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Go's declarations read left to right. It's been pointed out that C's
|
||||
read in a spiral! See <a href="http://c-faq.com/decl/spiral.anderson.html">
|
||||
The "Clockwise/Spiral Rule"</a> by David Anderson.
|
||||
</p>
|
@ -103,7 +103,7 @@ Guided tours of Go programs.
|
||||
<h4>Language</h4>
|
||||
<ul>
|
||||
<li><a href="http://blog.golang.org/2010/04/json-rpc-tale-of-interfaces.html">JSON-RPC: a tale of interfaces</a></li>
|
||||
<li><a href="http://blog.golang.org/2010/07/gos-declaration-syntax.html">Go's Declaration Syntax</a></li>
|
||||
<li><a href="/doc/articles/gos_declaration_syntax.html">Go's Declaration Syntax</a></li>
|
||||
<li><a href="/doc/articles/defer_panic_recover.html">Defer, Panic, and Recover</a></li>
|
||||
<li><a href="http://blog.golang.org/2010/09/go-concurrency-patterns-timing-out-and.html">Go Concurrency Patterns: Timing out, moving on</a></li>
|
||||
<li><a href="/doc/articles/slices_usage_and_internals.html">Go Slices: usage and internals</a></li>
|
||||
|
@ -1524,7 +1524,7 @@ and <code>chan</code> keep things clear.
|
||||
|
||||
<p>
|
||||
See the article about
|
||||
<a href="http://blog.golang.org/2010/07/gos-declaration-syntax.html">Go's Declaration Syntax</a>
|
||||
<a href="/doc/articles/gos_declaration_syntax.html">Go's Declaration Syntax</a>
|
||||
for more details.
|
||||
</p>
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user