【译】Go语言声明语法
引言
Go語言的初學者可能會好奇為什么Go的類型聲明語法和傳統的C系語言不同。本篇文章我們將比較這兩種不同的類型聲明方式,解釋為什么Go的聲明會是這樣子。
C語法
首先,我們談談C的語法。C采用了一種不同尋常且聰明的類型聲明語法。它沒有使用特殊語法來描述變量類型,而是使用一個包含要聲明變量的表達式,并陳述這個表達式的類型。因此
int x; 復制代碼聲明x是一個int,因為表達式'X'是int類型。通常,為了弄清楚如何聲明一個新變量,首先得寫一個包含這個變量的表達式,這個表達式本身是一個基礎類型。然后,把這個基礎類型放在左邊,表達式放在右邊。
因此,下面的幾個類型聲明:
聲明p是一個int指針,因為表達式”*p“是int類型。而a是一個int數組,因為a[3](忽略其中的數字,這個數字是用來指定數組的大小的)表達式是int類型。
再來看看函數。最開始,C的函數聲明將參數的類型寫在小括號外,像這樣:
同樣的,我們看到main是一個函數,因為表達式main(argc, argv)返回一個int。現在的寫法是:
int main(int argc, char *argv[]) { /* ... */ } 復制代碼但基本結構都是一樣的。
這種語法在類型簡單的時候沒有問題,但在復雜的情況下很容易讓人迷惑。一個著名的例子就是聲明一個函數指針。根據以上規則,應該這么寫:
其中,fp是一個函數指針,因為表達式(*fp)(a,b)表示調用一個返回int的函數。那如果fp的某個參數也是一個函數呢?
int (*fp)(int (*ff)(int x, int y), int b) 復制代碼這就開始變得難以讀懂了。
當然,我們在聲明一個函數時可以去掉參數的名字,所以main可以這樣聲明
回憶一下,參數argv的聲明是這樣的,
char *argv[] 復制代碼所以你從這個聲明語句的中間去掉了變量的名字來構建它的類型。這樣如果你需要聲明一個char *[]的變量,你得把變量名放在char *[]中間,顯然這樣不夠直觀。
如果不聲明參數的名字,來看看fp的聲明會是什么樣?
這樣不僅不知道變量名該放在哪
int (*)(int, int) 復制代碼而且很難看出來這是在聲明一個函數指針。進一步,如果該函數的返回類型也是一個函數指針呢?
int (*(*fp)(int (*)(int, int), int))(int, int) 復制代碼這里甚至很難看出這其中變量名為fp。
這樣精心構建的例子還有很多,這就說明C的變量聲明語法能有多復雜。
還有一點學要提下,就是類型轉換和類型聲明語法是一樣的,這使得C語言很難解析表達式中的類型轉換,這也就是為什么C系語言總是在類型轉換的時候打括號,例如
Go語法
非C系語言聲明一個類型聲明的語法通常是很清晰的。首先給出變量名字,然后接一個冒號,后面是類型。那么,上面的例子就變成了這樣(在一個解釋型的函數語言中)
x: int p: pointer to int a: array[3] of int 復制代碼這些聲明非常清晰,你只需要從左向右讀。Go借鑒了這些,但更簡潔,去掉了冒號和一些關鍵字:
x int p *int a [3]int 復制代碼[3]int的形式和如何在表達式中使用變量a沒有什么直接的聯系。通過這種分開的語法,表達的更清楚。
再看看函數。將上面的main函數使用Go表達出來(Go中main是沒有參數的):
表面上看起來似乎和C沒有多大差別,僅僅把char改成了strings。但Go版本更加適合從左向右閱讀:
main函數接受一個int和一個strings的切片,返回一個int。
去掉參數名也一樣簡潔,因為變量名永遠在聲明的最前面,所以這不會造成迷惑。
這種從左到右的聲明語法有一個非常實用的地方:當類型非常復雜的時候,它也能很清晰。下面是一個函數變量的聲明(類比C中的函數指針):
f func (func(int, int) int, int) int 復制代碼或者返回值也是一個函數
f func(func(int, int) int, int) func(int, int) int 復制代碼從左到右讀起來依然非常清晰,變量的名字也非常明顯,因為它總是第一次出現。
類型轉換和表達式之間的語法區別使得寫和調用閉包都非常容易:
指針
指針是規則中的一個特例。普通的,比如數組和切片,Go的類型聲明把中括號放在類型的左邊,但在表達式中會放在類型的右邊:
var a []int x = a[1] 復制代碼為了使用者更加熟悉,Go的指針也使用了C中*概念,但這就沒有了上面的顛倒規則,因此使用指針是這樣的:
var p *int x = *p 復制代碼而不是這樣的
var p *int x = p* 復制代碼因為后綴*與乘法沖突。我們也曾使用過^這個符合,例如:
var p ^int x = p^ 復制代碼或許我們應該這么寫(使用其他的符號表示異或),因為在類型聲明和表達式中同時使用前綴*在一些情況下會很復雜。例如
[]int("hi") 復制代碼為了類型轉換,如果類型以*開頭就必須加括號:
(*int)(nil) 復制代碼如果不使用*作為指針語法,那就不用打括號了。
所以Go的指針嘗試接近C的形式,這就意味著不能完全去掉區分類型聲明和表達式的括號。 總的來說,我們相信Go的類型聲明語法比C更容易理解,特別是在復雜的情況下。
總結
Go的類型聲明語法是從左到右的,而C的類型聲明語法則是螺旋形的!具體參考David Anderson的這篇文章 The "Clockwise/Spiral Rule"
個人總結
Go語言類型聲明語法中,永遠將變量名放在最前面,這比C系語言更加清晰,更適合從左向右閱讀。
但是類型轉換似乎沒有多大區別,都需要括號,只是括號的對象不同而已:
原文
Go's Declaration Syntax
總結
以上是生活随笔為你收集整理的【译】Go语言声明语法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 长春初二数学辅导班|长春初二数学补习班
- 下一篇: 南京人工智能高等研究院孔慧:多向技术驱动