1. 程式人生 > >Go's Declaration Syntax

Go's Declaration Syntax

7 July 2010

Introduction

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.

C syntax

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

int x;

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.

Thus, the declarations

int *p;
int a[3];

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.

What about functions? Originally, C's function declarations wrote the types of the arguments outside the parens, like this:

int main(argc, argv)
    int argc;
    char *argv[];
{ /* ... */ }

Again, we see that main is a function because the expression main(argc, argv) returns an int. In modern notation we'd write

int main(int argc, char *argv[]) { /* ... */ }

but the basic structure is the same.

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:

int (*fp)(int a, int b);

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?

int (*fp)(int (*ff)(int x, int y), int b)

That's starting to get hard to read.

Of course, we can leave out the name of the parameters when we declare a function, so main can be declared

int main(int, char *[])

Recall that argv is declared like this,

char *argv[]

so you drop the name from the middle 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.

And look what happens to fp's declaration if you don't name the parameters:

int (*fp)(int (*)(int, int), int)

Not only is it not obvious where to put the name inside

int (*)(int, int)

it's not exactly clear that it's a function pointer declaration at all. And what if the return type is a function pointer?

int (*(*fp)(int (*)(int, int), int))(int, int)

It's hard even to see that this declaration is about fp.

You can construct more elaborate examples but these should illustrate some of the difficulties that C's declaration syntax can introduce.

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

(int)M_PI

Go syntax

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)

x: int
p: pointer to int
a: array[3] of int

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:

x int
p *int
a [3]int

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.

Now consider functions. Let's transcribe the declaration for main as it would read in Go, although the real main function in Go takes no arguments:

func main(argc int, argv []string) int

Superficially that's not much different from C, other than the change from char arrays to strings, but it reads well from left to right:

function main takes an int and a slice of strings and returns an int.

Drop the parameter names and it's just as clear - they're always first so there's no confusion.

func main(int, []string) int

One merit 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):

f func(func(int,int) int, int) int

Or if f returns a function:

f func(func(int,int) int, int) func(int, int) int

It still reads clearly, from left to right, and it's always obvious which name is being declared - the name comes first.

The distinction between type and expression syntax makes it easy to write and invoke closures in Go:

sum := func(a, b int) int { return a+b } (3, 4)

Pointers

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:

var a []int
x = a[1]

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

var p *int
x = *p

We couldn't say

var p *int
x = p*

because that postfix * would conflate with multiplication. We could have used the Pascal ^, for example:

var p ^int
x = 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

[]int("hi")

as a conversion, one must parenthesize the type if it starts with a *:

(*int)(nil)

Had we been willing to give up * as pointer syntax, those parentheses would be unnecessary.

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.

Overall, though, we believe Go's type syntax is easier to understand than C's, especially when things get complicated.

Notes

Go's declarations read left to right. It's been pointed out that C's read in a spiral! See The "Clockwise/Spiral Rule" by David Anderson.

相關推薦

Go's Declaration Syntax

7 July 2010 Introduction Newcomers to Go wonder why the declaration syntax is different from the tradition estab

14 Go's Declaration Syntax

say bec related parameter using eal cau lan complete Go‘s Declaration Syntax 7 July 2010 Introduction Newcomers to Go wonder why the dec

Go實戰 golang中使用WebSocket實時聊天室 gorilla/websocket nkovacs/go s

                     生命不止,繼續 go go go!!!其實,早就應該跟大家分享golang中關於websocket的使用,但是一直不知道從何入手,也不能夠很清晰的描述出來。今天就淺嘗輒止,通過第三方庫實現websocket。WebSocketWebSocket協議是基於TCP的一種新

Gos hidden #pragmas

This is an article about compiler directives; or as they are commonly known, pragmas. It’s derived from a talk of a similar name that I gave last year

Maybe adding generics to Go IS about syntax after all

This is a short response to the recently announced Go 2 generics draft proposals Update: This proposal is incomplete. It cannot replace two common use

Getting to Go: The Journey of Go's Garbage Collector

12 July 2018 This is the transcript from the keynote I gave at the International Symposium on Memory Managem

The new kid in town — Gos sync.Map

The new kid in town — Go’s sync.MapA learning and exploratory analysis of the new sync.Map type in Go 1.9.Go 1.9 is out now and I was eager to get my hands

HDU 1824 Let's go home (2-SAT判定)

記錄 arch 對數 ref ise stack top code any Let‘s go home Time Limit: 10000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Othe

let's go, learn liunx and database today.

data 心情 裏的 申請 mil str tab log liunx 剛剛申請註冊博客記錄一下此時此刻的心情,哇.....太開心。今天開始學習的第一天吧,明天開始寫第一篇隨記,希望我在這裏的足跡伴我成長。加油.....let's go, learn l

Let`s go!

lin for man while esp cau with line HERE Here`s to the crazy ones.The misfits.The rebels.The troublemakers.The round pegs in the square h

Laravel + go-micro + grpc 實踐基於 Zipkin 的分布式鏈路追蹤系統 摘自https://mp.weixin.qq.com/s/JkLMNabnYbod-b4syMB3Hw?

啟動 quest -o 我們 clone maven 變慢 關鍵詞 擴展 分布式調用鏈跟蹤系統,屬於監控系統的一類。系統架構逐步演進時,後期形態往往是一個平臺由很多不同的服務、組件構成,用戶請求過來後,可能會經過其中多個服務,如圖 不過,出問題時往往很難排查,如整

Laravel + go-micro + grpc 實踐基於 Zipkin 的分散式鏈路追蹤系統 摘自https://mp.weixin.qq.com/s/JkLMNabnYbod-b4syMB3Hw?

分散式呼叫鏈跟蹤系統,屬於監控系統的一類。系統架構逐步演進時,後期形態往往是一個平臺由很多不同的服務、元件構成,使用者請求過來後,可能會經過其中多個服務,如圖     不過,出問題時往往很難排查,如整個請求變慢、偶爾報錯、不可用等,我們很難得知具體是由哪一個或哪些服務引起的,通常

Python 3's f-Strings: An Improved String Formatting Syntax (Guide)

source: https://realpython.com/python-f-strings/ As of Python 3.6, f-strings are a great new way to format strings. Not only are they more read

idea cannot find declaration to go to |使用Ctrl+左鍵/Ctrl+B都不能進入類或者方法

n to go to |使用Ctrl+左鍵/Ctrl+B都不能進入類或者方法 情景再現: 由於有多個project,並且這些project屬於不同的dir,這些dir還有擁有一個root dir,所以是檔案結構很整齊(每個project都是maven的) 最開始的imp

繼續,Let's Go!

-- 5.7.23 select version(); -- 非主鍵形式的自增欄位 create table test3 (  id int auto_increment not null,  str varchar(2),  key(id) ); -- 自增預設從1開始

Pycharm Cannot find declaration to go to的部分原因

試了一個網上的修改Source root 還有直譯器沒啥用,發現可能是程式碼本身的問題,當示例後者物件None或者可能不存在是,就會提示Cannot find declaration to go to applicant = None #

【文藝學生】Learning with exploration, and go ahead with learning. Let's progress together! :)

文藝學生 Learning with exploration, and go ahead with learning. Let's progress together! :)

hdu 1824 Let's go home (2-sate)

小時候,鄉愁是一枚小小的郵票,我在這頭,母親在那頭。                         —— 余光中 集訓是辛苦的,道路是坎坷的,休息還是必須的。經過一段時間的訓練,lcy決定讓大家回家放鬆一下,但是訓練還是得照常進行,lcy想出瞭如下回家規定,每一個隊(三人一隊)或者隊長留下或者其餘兩名隊員

oracel 密碼延時特性;更改 pfile導致啟動報錯ORA-49601: syntax error: found "%s": expecting one of: "%s" etc

olap star eve sysdba rev shutdown options man address [oracle@db ~]$ sqlplus / as sysdba SQL*Plus: Release 11.2.0.4.0 Production on Wed D