Spesifikasi Bahasa Pemrograman Go

Versi 2018 November 16

Pendahuluan

Dokumen ini adalah referensi manual untuk bahasa pemrograman Go. Untuk informasi dan dokumentasi lainnya lihat golang.org (Inggris) atau golang-id.org (Indonesia).

Go adalah bahasa pemrograman yang dirancang untuk pemrograman sistem. Go adalah bahasa dengan tipe yang kuat dan dengan garbage-collection dan mendukung pemrograman konkuren secara eksplisit. Program dibangun dari paket-paket, yang membolehkan ketergantungan paket secara efisien.

Tata-bahasa dari Go padat dan teratur, membuatnya mudah untuk dianalisis oleh perkakas-perkakas otomatis seperti lingkungan pengembangan integrasi (IDE).

Notasi

Spesifikasi sintaksis menggunakan Extended Backus-Naur Form (EBNF):

Production  = production_name "=" [ Expression ] "." .
Expression  = Alternative { "|" Alternative } .
Alternative = Term { Term } .
Term        = production_name | token [ "…" token ] | Group | Option | Repetition .
Group       = "(" Expression ")" .
Option      = "[" Expression "]" .
Repetition  = "{" Expression "}" .

Production adalah ekspresi yang dibangun dari sekumpulan term dan operator pendukung berikut, secara berurut:

|   alternasi
()  pengelompokan
[]  opsi (0 atau 1 kali)
{}  pengulangan (0 sampai n kali)

Nama production_name dengan huruf kecil digunakan untuk mengidentifikasi token leksikal. production_name dengan CamelCase adalah non-terminal (tidak berakhir). Token leksikal dibungkus dalam tanda kutip ganda " " atau kutip terbalik ``.

Bentuk a … b merepresentasikan kumpulan huruf alternatif dari a sampai b. Elipsis horizontal juga digunakan dalam spesifikasi ini untuk secara informal menandakan enumerasi atau kode yang dipotong. Karakter (berlawanan dengan tiga karakter …​) bukanlah token dari bahasa Go.

Representasi sumber kode

Sumber kode adalah teks Unicode dalam UTF-8. Teks tersebut tidak kanonis, sehingga titik kode dengan aksen berbeda dengan karakter yang sama yang dibangun dari menggabungkan sebuah aksen dan sebuah huruf; keduanya dianggap sebagai dua titik kode. Demi kemudahan, dokumen ini menggunakan istilah karakter untuk mengacu pada titik kode Unicode dalam teks sumber kode.

Setiap titik kode berbeda satu dengan yang lainnya; misalnya, huruf besar dan kecil adalah karakter yang berbeda.

Batasan implementasi: Untuk kompatibilitas dengan perkakas lainnya, compiler mungkin tidak membolehkan karakter NUL (U+0000) dalam teks sumber kode.

Batasan implementasi: Untuk kompatibilitas dengan perkakas lainnya, compiler bisa mengindahkan penanda urutan byte untuk UTF-8-encoded (U+FEFF) jika ia adalah titik kode Unicode pertama dalam teks sumber kode. Penanda urutan byte bisa saja tidak dibolehkan dalam sumber kode.

Karakter

Istilah berikut digunakan untuk menandakan kelas karakter Unicode tertentu:

newline        = /* titik kode Unicode U+000A untuk baris baru */ .
unicode_char   = /* titik kode Unicode apa pun kecuali baris baru */ .
unicode_letter = /* titik kode Unicode dikelompokkan sebagai "Letter" (huruf) */ .
unicode_digit  = /* titik kode Unicode dikelompokkan sebagai "Number, decimal digit" */ .

The Unicode Standard 8.0, Bagian 4.5 "General Category" mendefinisikan sekumpulan kategori karakter. Go mengenali semua karakter dalam kategori Letter berikut Lu, Ll, Lt, Lm, atau Lo sebagai huruf Unicode, dan yang berada dalam kategori Number Nd sebagai angka Unicode.

Huruf dan angka

Karakter garis bawah _ (U+005F) dianggap sebagai huruf.

letter        = unicode_letter | "_" .
decimal_digit = "0" … "9" .
octal_digit   = "0" … "7" .
hex_digit     = "0" … "9" | "A" … "F" | "a" … "f" .

Elemen leksikal

Komentar

Komentar berfungsi sebagai dokumentasi program. Ada dua bentuk komentar:

  1. Komentar baris dimulai dengan urutan karakter // dan berakhir pada ujung baris.

  2. Komentar umum dimulai dengan urutan karakter /* dan berakhir dengan urutan karakter */ yang pertama.

Komentar tidak boleh dimulai dalam sebuah rune atau string, atau di dalam komentar yang lain. Komentar umum tanpa baris baru berarti memiliki sebuah spasi. Komentar lainnya akan diartikan berada pada baris baru.

Token

Token-token membentuk kosakata dari bahasa Go. Ada empat kelas token: identifiers (pengidentifikasi), keywords (kata-kunci), operators dan punctuation (operator dan tanda baca), dan literal (literal). Karakter kosong yang dibentuk dari spasi (U+0020), tab horizontal (U+0009), carriage returns (U+0009), dan baris baru (U+000A), diindahkan kecuali ia memisahkan token-token yang bila digabungkan menjadi sebuah token. Juga, sebuah baris baru atau akhir dari berkas bisa memicu titik-koma. Saat memecah input menjadi token-token, token selanjutnya adalah urutan karakter terpanjang yang membentuk sebuah token yang valid.

Titik-koma

Tata-bahasa formal menggunakan titik-koma ";" sebagai penanda akhir pada beberapa pernyataan. Program Go bisa menghilangkan titik-koma tersebut menggunakan dua aturan berikut:

  1. Saat input terpecah menjadi token-token, sebuah titik-koma secara otomatis langsung ditambahkan ke dalam aliran token setelah token terakhir pada sebuah baris jika token tersebut adalah

  2. Untuk membolehkan perintah yang kompleks menghabiskan satu baris, titik-koma bisa dihilangkan sebelum ditutup oleh ")" atau "}"

Untuk merefleksikan penggunaan idiomatis, contoh kode dalam dokumen ini tidak menggunakan titik-koma menggunakan aturan di atas.

Pengidentifikasi memberi nama entitas seperti variabel dan tipe. Sebuah pengidentifikasi adalah sebuah urutan satu atau lebih huruf dan angka. Karakter pertama dalam sebuah pengidentifikasi haruslah huruf.

identifier = letter { letter | unicode_digit } .
a
_x9
ThisVariableIsExported
αβ

Beberapa pengidentifikasi telah dideklarasikan sebelumnya.

Kata kunci

Kata kunci berikut telah disiapkan dan tidak bisa digunakan sebagai pengidentifikasi:

break        default      func         interface    select
case         defer        go           map          struct
chan         else         goto         package      switch
const        fallthrough  if           range        type
continue     for          import       return       var

Operator dan tanda baca

Urutan karakter berikut merepresentasikan operator (termasuk operator penempatan) dan tanda baca:

+    &     +=    &=     &&    ==    !=    (    )
-    |     -=    |=     ||    <     <=    [    ]
*    ^     *=    ^=     <-    >     >=    {    }
/    <<    /=    <<=    ++    =     :=    ,    ;
%    >>    %=    >>=    --    !     ...   .    :
     &^          &^=

Literal integer

Literal integer adalah urutan angka merepresentasikan sebuah konstan integer. Beberapa opsi prefiks menyatakan basis non-desimal: 0 untuk oktal, 0x atau 0x untuk heksadesimal. Dalam literal heksadesimal, huruf a-f dan A-F merepresentasikan nilai antara 10 sampai 155.

int_lit     = decimal_lit | octal_lit | hex_lit .
decimal_lit = ( "1" … "9" ) { decimal_digit } .
octal_lit   = "0" { octal_digit } .
hex_lit     = "0" ( "x" | "X" ) hex_digit { hex_digit } .
42
0600
0xBadFace
170141183460469231731687303715884105727

Literal floating-point

Literal floating-point adalah representasi desimal dari konstan _floating-point_. Ia memiliki bagian integer, titik desimal, bagian pecahan, dan bagian eksponen. Bagian integer dan pecahan terdiri dari angka desimal; bagian eksponen yaitu e atau E diikuti dengan nilai eksponen. Salah satu bagian integer atau pecahan bisa diindahkan; salah satu bagian pecahan atau eksponen bisa diindahkan juga.

float_lit = decimals "." [ decimals ] [ exponent ] |
            decimals exponent |
            "." decimals [ exponent ] .
decimals  = decimal_digit { decimal_digit } .
exponent  = ( "e" | "E" ) [ "+" | "-" ] decimals .
0.
72.40
072.40  // == 72.40
2.71828
1.e+0
6.67428e-11
1E6
.25
.12345E+5

Literal imajiner

Literal imajiner merepresentasikan bagian imajiner dari konstan kompleks. Ia terdiri dari literal integer atau floating-point diikuti oleh huruf kecil i. Nilai dari literal imajiner yaitu nilai dari literal integer atau floating-point dikalikan dengan unit imajiner i.

imaginary_lit = (decimal_digits | int_lit | float_lit) "i" .

Untuk kompatibilitas, bagian integer dari literal imajiner yang semuanya terdiri dari angka desimal (dan kemungkinan garis bawah) dianggap sebagai integer desimal, walaupun dimulai dengan 0.

0i
0123i         // == 123i untuk kompatibilitas
0o123i        // == 0o123 * 1i == 83i
0xabci        // == 0xabc * 1i == 2748i
0.i
2.71828i
1.e+0i
6.67428e-11i
1E6i
.25i
.12345E+5i
0x1p-2i       // == 0x1p-2 * 1i == 0.25i

Literal rune

Literal rune merepresentasikan sebuah konstan rune, sebuah nilai integer yang mengidentifikasi sebuah kode poin Unicode. Literal rune diekspresikan dengan satu atau lebih karakter yang ditutup dengan tanda kutip tunggal, seperti 'x' atau '\n'. Di dalam tanda kutip tersebut, karakter apa pun dapat ditulis kecuali baris baru dan tanda kutip tunggal itu sendiri. Sebuah karakter dengan kutip merepresentasikan nilai Unicode dari karakter itu sendiri, sementara seurutan karakter yang dimulai dengan garis miring terbalik (backslash) akan ditulis nilainya menjadi beragam format.

Bentuk sederhana dari rune merepresentasikan karakter tunggal antara tanda kutip; secara teks sumber kode dari Go adalah karakter Unicode yang ditulis dalam UTF-8, multipel UTF-8 byte bisa merepresentasikan sebuah nilai integer. Misalnya, literal 'a' menyimpan sebuah byte yang merepresentasikan sebuah literal a, Unicode U+0061, dengan nilai 0x61; sementara 'ä' menyimpan dua byte (0xc3 0xa4) yang merepresentasikan literal a-dwititik, U+00E4, nilai 0xe4.

Beberapa backslash membolehkan nilai beragam ditulis sebagai teks ASCII. Ada empat cara untuk merepresentasikan nilai integer sebagai konstan numerik: \x diikuti dengan dua digit heksadesimal; \u diikuti dengan empat digit heksadesimal; \U diikuti dengan delapan heksadesimal, dan backslash kosong \ diikuti dengan tiga digit oktal. Pada setiap kasus tersebut nilai dari literal adalah nilai yang direpresentasikan oleh digit pada basis yang berhubungan.

Walaupun representasi tersebut semua hasilnya adalah sebuah integer, mereka memiliki rentang yang berbeda. Oktal harus merepresentasikan sebuah nilai antara 0 dan 255 secara inklusif. Heksadesimal memenuhi kondisi ini secara konstruksi. \u dan \U merepresentasikan kode poin Unicode sehingga beberapa nilai adalah ilegal, khususnya nilai di atas 0x10FFFF dan surrogate half (bagian setengah atas dari empat heksadesimal).

Setelah tanda backslash, beberapa karakter tunggal merepresentasikan nilai spesial:

\a   U+0007 alert atau bel
\b   U+0008 backspace
\f   U+000C form feed
\n   U+000A line feed atau newline
\r   U+000D carriage return
\t   U+0009 tab horizontal
\v   U+000b tab vertikal
\\   U+005c backslash (garis miring terbalik)
\'   U+0027 tanda kutip tunggal (valid hanya pada literal rune)
\"   U+0022 tanda kutip ganda (valid hanya pada literal string)

Urutan karakter lain yang dimulai dengan sebuah backslash adalah ilegal di dalam literal rune.

rune_lit         = "'" ( unicode_value | byte_value ) "'" .
unicode_value    = unicode_char | little_u_value | big_u_value | escaped_char .
byte_value       = octal_byte_value | hex_byte_value .
octal_byte_value = `\` octal_digit octal_digit octal_digit .
hex_byte_value   = `\` "x" hex_digit hex_digit .
little_u_value   = `\` "u" hex_digit hex_digit hex_digit hex_digit .
big_u_value      = `\` "U" hex_digit hex_digit hex_digit hex_digit
                           hex_digit hex_digit hex_digit hex_digit .
escaped_char     = `\` ( "a" | "b" | "f" | "n" | "r" | "t" | "v" | `\` | "'" | `"` ) .
'a'
'ä'
'本'
'\t'
'\000'
'\007'
'\377'
'\x07'
'\xff'
'\u12e4'
'\U00101234'
'\''         // literal rune berisi sebuah karakter kutip
'aa'         // ilegal: terlalu banyak karaketer
'\xa'        // ilegal: terlalu sedikit digit heksadesimal
'\0'         // ilegal: terlalu sedikit digit oktal
'\uDFFF'     // ilegal: setengah
'\U00110000' // ilegal: invalid Unicode

Literal string

Literal string merepresentasikan konstan string yang didapat dari menggabungkan seurutan karakter-karakter. Ada dua bentuk string: literal string mentah dan literal string interpretasi.

Literal string mentah yaitu seurutan karakter antara kutip terbalik (aksen nontirus), seperti dalam `foo` . Di antara tanda kutip terbalik, karakter apa pun dapat muncul kecuali tanda kutip terbalik itu sendiri. Nilai dari literal string mentah tersebut yaitu string yang terdiri dari karakter-karakter yang tidak diinterpretasi (implisit UTF-8) di antara tanda kutip; pada khususnya, backslash tidak berfungsi dan string bisa memiliki baris baru. Karakter carriage return ('\r') di dalam literal string mentah diindahkan dari nilai string mentah.

Literal string interpretasi yaitu seurutan karakter antara tanda kutip ganda, seperti dalam"bar". Di antara tanda kutip, karakter apa pun bisa muncul kecuali baris baru dan tanda kutip ganda lepas (tanpa backslash). Teks antara tanda kutip membentuk nilai dari literal, dengan backslash diartikan seperti halnya dalam literal rune (kecuali \' adalah ilegal dan \" adalah legal), dengan batasan-batasan yang sama. Tiga-digit oktal (\nnn) dan dua-digit heksadesimal (\xnn) merepresentasikan byte individu dari string; karakter lepas lainnya merepresentasikan encoding UTF-8 (bisa jadi multi-byte) dari karakter-karakter tersebut. Maka di dalam literal string, \377 dan \xFF merepresentasikan nilai 0xFF=255, sementara ÿ, `\u00FF, \U000000FF dan \xc3\xbf merepresentasikan dua byte 0xc3 0xbf dari encoding karakter UTF-8 U+00FF.

string_lit             = raw_string_lit | interpreted_string_lit .
raw_string_lit         = "`" { unicode_char | newline } "`" .
interpreted_string_lit = `"` { unicode_value | byte_value } `"` .
`abc`                // sama dengan "abc"
`\n
\n`                  // sama dengan "\\n\n\\n"
"\n"
"\""                 // sama dengan `"`
"Hello, world!\n"
"日本語"
"\u65e5本\U00008a9e"
"\xff\u00FF"
"\uD800"             // ilegal: _surrogate half_
"\U00110000"         // ilegal: invalid kode poin Unicode

Contoh berikut merepresentasikan string yang sama:

"日本語"                                 // UTF-8 input teks
`日本語`                                // UTF-8 input teks sebagai literal mentah
"\u65e5\u672c\u8a9e"                    // kode poin Unicode eksplisit
"\U000065e5\U0000672c\U00008a9e"        // kode poin Unicode eksplisit
"\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e"  // UTF-8 byte eksplisit

Jika sumber kode merepresentasikan sebuah karakter sebagai dua kode poin, seperti sebuah gabungan bentuk mengikutkan aksen dan sebuah huruf, hasilnya akan eror jika ditempatkan dalam sebuah literal rune (karena bukanlah kode poin tunggal), dan akan muncul sebagai dua kode poin jika ditempatkan dalam literal string.

Konstan

Ada konstan boolean, konstan rune, konstan integer, konstan floating-point, konstan complex, dan konstan string. Konstan rune, integer, floating-point, dan complex secara kolektif disebut dengan konstan numerik.

Nilai sebuah konstan direpresentasikan oleh sebuah literal rune, integer, floating-point, imajiner, atau string, sebuah pengidentifikasi yang menyatakan sebuah konstan, sebuah ekspresi konstan, sebuah konversi dengan hasil sebuah konstan, atau nilai hasil dari fungsi bawaan seperti unsafe.Sizeof yang diterapkan terhadap nilai apa pun, cap atau len yang diterapkan pada beberapa ekspresi, real dan imag yang diterapkan pada konstan complex dan complex yang diterapkan pada konstan numerik. Nilai boolean direpresentasikan dengan konstan true dan false. Identifikasi iota menyatakan konstan integer.

Secara umum, konstan complex adalah sebuah bentuk dari ekspresi konstan dan didiskusikan di bagian tersebut.

Konstan numerik merepresentasikan nilai eksak dari presisi beragam dan tidak overflow. Karena itu, tidak ada konstan yang menyatakan nilai negatif nol IEEE-754, infinity, dan nilai bukan-angka.

Konstan bisa bertipe atau tak bertipe. Konstan harfiah, true, false, iota, dan beberapa ekspresi konstan yang berisi hanya operan konstan tanpa tipe adalah konstan tak bertipe.

Sebuah konstan bisa diberikan tipe secara eksplisit dengan deklarasi konstan atau konversi, atau secara implisit bila digunakan dalam sebuah deklarasi variabel atau dalam sebuah penempatan atau sebagai sebuah operan dalam sebuah ekspresi. Jika nilai konstan tidak dapat direpresentasikan sebagai sebuah nilai dari tipe maka ia akan eror.

Konstan tak bertipe memiliki tipe bawaan yaitu tipe yang mana konstan secara implisit dikonversi dalam konteks di mana nilai bertipe dibutuhkan, misalnya, dalam deklarasi variabel singkat seperti i := 0 yang mana tidak ada tipe eksplisit. Tipe bawaan dari konstan tak bertipe adalah bool, rune, int, float64, complex128 atau string, bergantung kepada apakah ia adalah sebuah konstan boolean, rune, integer, floating-point, complex, atau string.

Batasan implementasi: Walaupun konstan numerik memiliki presisi beragam dalam bahasa Go, compiler bisa saja mengimplementasikan mereka menggunakan representasi internal dengan presisi terbatas. Dengan kata lain, setiap implementasi harus:

  • Merepresentasikan konstan integer paling tidak 256 bit.

  • Merepresentasikan konstan floating-point, termasuk bagian dari konstan complex, dengan mantisa setidaknya 256 bit dan sebuah eksponen binary signed setidaknya 16 bit.

  • Kembalikan eror bila tidak bisa merepresentasikan konstan integer secara tepat.

  • Kembalikan eror bila tidak bisa merepresentasikan sebuah konstan floating-point atau complex karena overflow.

  • Bulatkan ke konstan terdekat yang dapat direpresentasikan jika tidak bisa merepresentasikan konstan floating-point atau complex dikarenakan limit atau presisi.

Kebutuhan-kebutuhan di atas berlaku baik terhadap konstan harfiah dan terhadap hasil dari mengevaluasi ekspresi konstan.

Variabel

Sebuah variabel adalah sebuah lokasi penampung untuk menyimpan sebuah nilai. Kumpulan dari nilai yang diperbolehkan ditentukan oleh tipe variabel.

Sebuah deklarasi variabel atau penanda dari deklarasi fungsi (pada parameter dan kembalian dari fungsi), atau fungsi literal mencadangkan penampung untuk sebuah variabel bernama. Memanggil fungsi bawaan new atau mengambil alamat dari sebuah literal komposit mengalokasikan penampung untuk sebuah variabel pada saat runtime. Variabel anonim diacu lewat sebuah (bisa jadi implisit) pointer tak-langsung.

Variabel berstruktur dari tipe array, slice, dan struct memiliki elemen-elemen dan field-field yang bisa diakses secara individu. Setiap elemen tersebut berlaku seperti sebuah variabel.

Tipe statis (atau tipe) dari sebuah variabel yaitu tipe yang diberikan saat deklarasi, tipe yang diberikan pada saat pemanggilan new atau komposit literal, atau tipe dari sebuah elemen dari variabel berstruktur. Variabel dari tipe interface memiliki tipe dinamis berbeda, yaitu tipe konkret dari nilai yang diberikan ke variabel pada saat runtime (kecuali bila nilainya adalah nil, yang berarti tak bertipe). Tipe dinamis bisa beragam selama eksekusi namun nilai yang disimpan dalam variabel interface selalu ditempatkan ke tipe statis dari variabel.

var x interface{}  // x adalah nil dengan tipe statis interface{}
var v *T           // v bernilai nil, tipe statis *T
x = 42             // x bernilai 42 dan tipe dinamis int
x = v              // x bernilai (*T)(nil) dan tipe dinamis *T

Nilai variabel diambil dengan mengacu pada variabel dalam sebuah ekspresi; nilai dari variabel yaitu nilai terakhir yang ditempatkan ke variabel tersebut. Jika sebuah variabel belum diberi nilai, nilainya adalah nilai kosong dari tipe variabel.

Tipe

Sebuah tipe menentukan sekumpulan nilai berikut dengan operasi dan method yang spesifik terhadap nilainya. Sebuah tipe bisa ditulis dengan sebuah nama tipe, jika ada, atau dispesifikasikan menggunakan literal tipe, yang membentuk sebuah tipe dari tipe-tipe yang telah ada.

Type      = TypeName | TypeLit | "(" Type ")" .
TypeName  = identifier | QualifiedIdent .
TypeLit   = ArrayType | StructType | PointerType | FunctionType | InterfaceType |
	    SliceType | MapType | ChannelType .

Bahasa Go mendeklarasikan beberapa nama tipe. Nama tipe lainnya diperkenalkan lewat deklarasi tipe. Tipe komposit -- tipe array, struct, pointer, fungsi, interface, slice, map, dan channel -- bisa dibangun menggunakan literal tipe.

Setiap tipe T memiliki tipe dasar: Jika T adalah salah satu tipe boolean, numerik, string, literal; make tipe dasar dari T itu sendiri adalah T. Selain itu, tipe dasar dari T yaitu tipe yang diberikan saat deklarasi.

type (
	A1 = string
	A2 = A1
)

type (
	B1 string
	B2 B1
	B3 []B1
	B4 B3
)

Tipe dasar dari string, A1, A2, B1, dan B2 adalah string. Tipe dasar dari []B1, B3, dan B4 yaitu []B1.

Kumpulan method

Sebuah tipe bisa memiliki sekumpulan method. Kumpulan method dari tipe interface yaitu interface-nya sendiri. Kumpulan method dari tipe T terdiri dari semua method-method yang dideklarasikan pada penerima (receiver) tipe T. Kumpulan method dari tipe pointer *T yaitu semua method yang dideklarasikan dengan penerima *T atau T (ia berisi juga kumpulan method dari T). Aturan yang sama juga berlaku untuk struct yang berisi field-field yang ditanam (embed), seperti yang dijelaskan dalam bagian tipe struct. Tipe apa pun memiliki kumpulan method kosong. Di dalam sebuah kumpulan method, setiap method harus memiliki nama method yang unik dan tidak kosong.

Kumpulan method dari sebuah tipe menentukan interface yang diimplementasi oleh tipe tersebut dan method tersebut bisa dipanggil menggunakan penerima dari tipe tersebut.

Tipe boolean

Sebuah tipe boolean merepresentasikan kumpulan nilai Boolean yang dilambangkan oleh konstan true dan false. Tipe boolean dikenal sebagai bool; ia adalah tipe terdefinisi.

Tipe numerik

Tipe numerik merepresentasikan kumpulan nilai integer atau floating-point. Deklarasi dari tipe numerik, yang bergantung pada arsitektur, yaitu:

uint8       kumpulan semua unsigned  8-bit integer (0 sampai 255)
uint16      kumpulan semua unsigned 16-bit integer (0 sampai 65535)
uint32      kumpulan semua unsigned 32-bit integer (0 sampai 4294967295)
uint64      kumpulan semua unsigned 64-bit integer (0 sampai 18446744073709551615)

int8        kumpulan semua signed  8-bit integers (-128 sampai 127)
int16       kumpulan semua signed 16-bit integers (-32768 sampai 32767)
int32       kumpulan semua signed 32-bit integers (-2147483648 sampai 2147483647)
int64       kumpulan semua signed 64-bit integers (-9223372036854775808 sampai 9223372036854775807)

float32     kumpulan semua IEEE-754 32-bit bilangan floating-point
float64     kumpulan semua IEEE-754 64-bit bilangan floating-point

complex64   kumpulan semua bilangan complex dengan float32 pada bagian real dan imajiner
complex128  kumpulan semua bilangan complex dengan float64 pada bagian real dan imajiner

byte        alias untuk uint8
rune        alias untuk int32

Nilai dari n-bit integer yaitu n bit lebarnya dan direpresentasikan menggunakan aritmetika komplemen ganda.

Ada juga kumpulan tipe numerik yang ukurannya tergantung pada arsitektur:

uint     bisa 32 atau 64 bit
int      berukuran sama dengan uint
uintptr  unsigned integer cukup besar menyimpan bit dari nilai pointer

Untuk menghindari masalah portabilitas semua tipe numerik adalah tipe terdefinisi dan makanya berbeda kecuali byte, yang merupakan alias dari uint8, dan rune, yang merupakan alias untuk uint32. Konversi eksplisit dibutuhkan saat tipe numerik berbeda bertemu pada sebuah ekspresi atau pernyataan. Misalnya, int32 dan int bukanlah tipe yang sama walaupun keduanya berukuran sama pada arsitektur tertentu.

Tipe string

Tipe string merepresentasikan kumpulan nilai string. Nilai sebuah string yaitu (bisa kosong) urutan dari byte. Jumlah byte disebut juga panjang dari string dan tidak pernah negatif. String adalah immutable (tetap): sekali dibuat, maka tidak akan bisa lagi diubah isinya. Tipe string yaitu string; ia adalah tipe terdefinisi.

Panjang dari string s dapat diketahui lewat fungsi bawaan len. Panjangnya akan konstan bila string tersebut adalah konstan. Isi byte dari string dapat diakses dengan indeks 0 sampai len(s)-1. Mengambil alamat byte pada string adalah ilegal; jika s[i] adalah byte ke i dari string, maka &s[i] tidak valid.

Tipe array

Sebuah array yaitu urutan elemen dari sebuah tipe yang jumlahnya tetap. Jumlah dari elemen disebut juga dengan panjang array dan tidak pernah negatif.

ArrayType   = "[" ArrayLength "]" ElementType .
ArrayLength = Expression .
ElementType = Type .

Panjang array adalah bagian dari tipe array; Ia harus berupa konstanta non-negatif yang direpresentasikan oleh nilai bertipe int. Panjang dari array a dapat diketahui dengan fungsi bawaan len. Setiap elemen array dapat diakses lewat indeks 0 sampai len(a)-1. Tipe array selalu satu dimensi namun bisa digabung untuk membentuk tipe multi-dimensi.

[32]byte
[2*N] struct { x, y int32 }
[1000]*float64
[3][5]int
[2][2][2]float64  // sama dengan [2]([2]([2]float64))

Tipe slice

Sebuah slice adalah penampung dari segmen bersambungan dari sebuah array dasar dan menyediakan akses ke elemen dari array tersebut. Sebuah tipe slice menandakan kumpulan dari semua bagian dari array dari tipe elemennya. Jumlah elemen disebut dengan panjang slice dan tidak pernah negatif. Nilai dari slice yang tidak diinisiasi yaitu nil.

SliceType = "[" "]" ElementType .

Panjang dari slice diketahui lewat fungsi bawaan len; tidak seperti array ia bisa berubah selama eksekusi. Elemen slice bisa diakses lewat integer indeks 0 sampai len(s)-1. Indeks slice dari elemen bisa kecil dari indeks dari elemen yang sama di array dasarnya.

Sebuah slice, setelah diinisiasi, selalu berasosiasi dengan array dasar yang menyimpan elemen-elemennya. Oleh karena itu sebuah slice berbagi tempat penyimpanan dengan array-nya dan dengan slice lain dari array yang sama; sebaliknya, array yang berbeda merepresentasikan tempat penyimpanan yang berbeda.

Array yang mendasari sebuah slice bisa diperlebar melebihi akhir dari slice. Konsep ini dikenal dengan kapasitas: ia adalah jumlah panjang dari slice dan panjang dari array-dasar slice; Sebuah slice yang panjangnya lebih dari kapasitas bisa dibuat dengan slicing (memotong) yang baru dari slice asli. Kapasitas dari slice a dapat diketahui menggunakan fungsi bawaan cap(a).

Slice baru bertipe T dapat diinisiasi menggunakan fungsi bawaan make, yang menerima tipe slice dan parameter yang menentukan panjang, dan opsi kapasitas. Sebuah slice yang dibuat dengan make selalu mengalokasikan array yang baru yang diacu oleh slice yang dikembalikan. Sehingga, mengeksekusi

make([]T, length, capacity)

menghasilkan slice yang sama seperti membuat array dan memotongnya, sehingga dua ekspresi berikut adalah sama:

make([]int, 50, 100)
new([100]int)[0:50]

Seperti halnya array, slice selalu satu-dimensi namun bisa bergabung membentuk objek dengan dimensi lebih tinggi. Lewat array dari array, array di dalamnya, selalu memiliki panjang yang sama; namun dengan slice dari slice (atau array dari slice), panjang di dalamnya bisa beragam secara dinamis. Lebih lanjut, slice paling dalam harus diinisiasi secara sendiri-sendiri.

Tipe struct

Sebuah struct yaitu urutan dari elemen bernama, disebut field, tiap-tiap field memiliki nama dan tipe. Nama field bisa secara eksplisit (IdentifierList) atau secara implisit (EmbeddedField). Dalam sebuah struct, nama field yang tidak kosong haruslah unik.

StructType    = "struct" "{" { FieldDecl ";" } "}" .
FieldDecl     = (IdentifierList Type | EmbeddedField) [ Tag ] .
EmbeddedField = [ "*" ] TypeName .
Tag           = string_lit .
// Sebuah struct kosong.
struct {}

// Sebuah struct dengan 6 field.
struct {
	x, y int
	u float32
	_ float32  // padding
	A *[]int
	F func()
}

Field dengan tipe namun tanpa nama disebut dengan field tertanam. Field tertanam haruslah tipe bernama T atau sebagai pointer ke tipe bernama yang bukan interface *T, dan T itu sendiri bisa saja bukan tipe pointer. Nama tipe pada field tertanam berlaku sebagai nama field.

// Sebuah struct dengan empat field tertanam bertipe T1, *T2, P.T3 dan *P.T4
struct {
	T1        // nama fieldnya yaitu T1
	*T2       // nama fieldnya yaitu T2
	P.T3      // nama fieldnya yaitu T3
	*P.T4     // nama field nya yaitu T4
	x, y int  // nama field nya yaitu x dan y
}

Deklarasi berikut ilegal karena nama field harus unik dalam sebuah tipe struct:

struct {
	T     // konflik dengan field tertanam *T dan *P.T
	*T    // konflik dengan field tertanam T dan *P.T
	*P.T  // konflik dengan field tertanam T dan *T
}

Sebuah field atau method f dari field tertanam dalam sebuah struct x disebut dipromosikan jika x.f adalah selektor yang legal yang menandakan field atau method f tersebut.

Field yang dipromosikan berlaku seperti field biasa kecuali ia tidak bisa digunakan sebagai nama field di dalam literal komposit dari struct.

Diberikan sebuah tipe struct S dan tipe terdefinisi T, method-method yang dipromosikan yang masuk ke dalam kumpulan method dari struct S yaitu:

  • Jika S berisi field tertanam T, kumpulan method dari S dan *S mengikutkan method-method promosi dari T. Kumpulan method dari *S juga mengikutkan method promosi dari *T.

  • Jika S berisi field tertanam T, kumpulan method dari S dan *S keduanya mengikutkan method promosi dari T atau *T.

Deklarasi field bisa ditambah dengan literal string tag, yang menjadi atribut dari field. Tag kosong berarti tidak ada tag. Tag bisa diakses lewat interface refleksi dan ambil bagian dalam identitas tipe dari struct.

struct {
	x, y float64 ""  // tag kosong sama dengan tanpa tag.
	name string  "string apa pun dibolehkan sebagai tag"
	_    [4]byte "ceci n'est pas un champ de structure"
}

// Sebuah struct untuk TimeStamp pada protocol buffer.
// String tag mendefinisikan field angka untuk protocol buffer;
// hal ini mengikuti konvensi yang dijelaskan oleh paket reflect.
struct {
	microsec  uint64 `protobuf:"1"`
	serverIP6 uint64 `protobuf:"2"`
}

Tipe pointer

Tipe pointer menandakan kumpulan semua pointer terhadap variabel dari tipe yang diberikan, disebut juga dengan tipe dasar dari pointer. Nilai dari pointer yang tidak diinisiasi adalah nil.

PointerType = "*" BaseType .
BaseType    = Type .
*Point
*[4]int

Tipe fungsi

Tipe fungsi menandakan kumpulan semua fungsi dengan tipe parameter dan kembalian yang sama. Nilai dari variabel bertipe fungsi yang tidak diinisiasi yaitu nil.

FunctionType   = "func" Signature .
Signature      = Parameters [ Result ] .
Result         = Parameters | Type .
Parameters     = "(" [ ParameterList [ "," ] ] ")" .
ParameterList  = ParameterDecl { "," ParameterDecl } .
ParameterDecl  = [ IdentifierList ] [ "..." ] Type .

Dalam parameter atau kembalian, nama (IdentifierList) haruslah ada atau tidak ada sama sekali. Jika ada, setiap nama mengacu untuk satu item (parameter atau kembalian) dari tipe dan semua nama haruslah unik. Jika tanpa nama, setiap tipe merepresentasikan satu item untuk tipe tersebut. Daftar parameter dan kembalian selalu dalam tanda kurung kecuali pada kembalian tunggal tanpa nama, ia bisa ditulis tanpa tanda kurung.

Parameter terakhir dalam penanda fungsi bisa memiliki tipe yang dimulai dengan "…​" . Fungsi dengan parameter tersebut disebut variadic dan bisa dipanggil tanpa atau lebih argumen dengan tipe yang sama.

func()
func(x int) int
func(a, _ int, z float32) bool
func(a, b int, z float32) (bool)
func(prefix string, values ...int)
func(a, b int, z float64, opt ...interface{}) (success bool)
func(int, int, float64) (float64, *[]int)
func(n int) func(p *T)

Tipe interface

Tipe interface menspesifikasikan sekumpulan method yang disebut dengan interface. Sebuah variabel bertipe interface dapat menyimpan tipe apa pun asalkan memiliki kumpulan method yang dimiliki oleh interface tersebut. Tipe tersebut dikatakan mengimplementasikan interface. Nilai dari variabel interface yang tidak diinisiasi adalah nil.

InterfaceType      = "interface" "{" { MethodSpec ";" } "}" .
MethodSpec         = MethodName Signature | InterfaceTypeName .
MethodName         = identifier .
InterfaceTypeName  = TypeName .

Dalam tipe interface, setiap method haruslah memiliki nama yang unik dan tidak- kosong.

// Sebuah interface berkas sederhana.
interface {
	Read(b Buffer) bool
	Write(b Buffer) bool
	Close()
}

Satu atau lebih tipe yang berbeda dapat mengimplementasikan sebuah interface. Misalnya, jika dua tipe S1 dan S2 memiliki sekumpulan method

func (p T) Read(b Buffer) bool { return … }
func (p T) Write(b Buffer) bool { return … }
func (p T) Close() { … }

(yang mana T bisa S1 atau S2) maka interface File dikatakan diimplementasikan oleh S1 dan S2, walaupun S1 dan S2 bisa saja punya method yang lain.

Sebuah tipe bisa mengimplementasikan satu atau lebih interface yang berbeda. Misalnya, semua tipe mengimplementasikan interface kosong:

interface{}

Hal yang sama, pada spesifikasi interface berikut, yang muncul dalam deklarasi tipe yang mendefinisikan sebuah interface bernama Locker:

type Locker interface {
	Lock()
	Unlock()
}

Jika S1 dan S2 juga mengimplementasikan

func (p T) Lock() { … }
func (p T) Unlock() { … }

maka mereka juga mengimplementasikan interface Locker dan juga interface File.

Sebuah interface T bisa menanam tipe interface bernama E. Hal ini disebut penanaman interface E dalam T; penanaman interface berarti menambahkan semua method (yang diekspor dan tidak diekspor) dari E ke interface T.

type ReadWriter interface {
	Read(b Buffer) bool
	Write(b Buffer) bool
}

type File interface {
	ReadWriter  // sama dengan menambahkan method-method dari ReadWriter
	Locker      // sama dengan menambahkan method-method dari Locker
	Close()
}

type LockedFile interface {
	Locker
	File        // ilegal: Lock, Unlock tidak unik
	Lock()      // ilegal: Lock tidak unik
}

Interface bertipe T tidak bisa secara rekursi menanam dirinya sendiri atau interface lain yang menanam T.

// ilegal: Bad tidak bisa menanam dirinya sendiri
type Bad interface {
	Bad
}

// ilegal: Bad1 tidak bisa menanam dirinya sendiri menggunakan Bad2
type Bad1 interface {
	Bad2
}
type Bad2 interface {
	Bad1
}

Tipe map

Sebuah map adalah gabungan tak urut dari elemen dari satu tipe, yang disebut tipe elemen, yang memiliki indeks berupa sekumpulan key unik dari tipe lainnya, disebut tipe key. Nilai dari map yang tidak diinisiasi yaitu nil.

MapType     = "map" "[" KeyType "]" ElementType .
KeyType     = Type .

Operator pembanding == dan != haruslah terdefinisi untuk operan dari tipe key; oleh karena itu tipe key tidak bisa berupa fungsi, map, atau slice. Jika tipe key adalah sebuah tipe interface, maka operator pembanding harus terdefinisi untuk nilai key dinamis; jika tidak akan menyebabkan run-time panic.

map[string]int
map[*T]struct{ x, y float64 }
map[string]interface{}

Jumlah elemen map disebut panjang. Untuk map m, jumlahnya bisa diambil dengan fungsi bawaan len dan ia bisa berubah selama eksekusi. Elemen bisa ditambah selama eksekusi menggunakan penempatan dan diambil dengan ekspresi indeks; dan elemen bisa dihapus dengan fungsi bawaan delete.

Nilai map yang baru dan kosong dibuat dengan fungsi bawaan make, yang menerima tipe map dan kapasitas opsional sebagai argumen:

make(map[string]int)
make(map[string]int, 100)

Kapasitas awal tidak ada batas ukurannya: map berkembang untuk mengakomodasi sejumlah item yang disimpannya, kecuali pada map yang nil. Map yang nil sama dengan map kosong kecuali ia tidak bisa ditambahkan dengan elemen.

Tipe channel

Sebuah channel menyediakan sebuah mekanisme untuk mengeksekusi fungsi secara konkuren untuk berkomunikasi dengan mengirim dan menerima nilai dari tipe elemen yang ditentukan. Nilai dari channel yang tidak diinisiasi yaitu nil.

ChannelType = ( "chan" | "chan" "<-" | "<-" "chan" ) ElementType .

Operator opsional <- menspesifikasikan arah channel, kirim atau terima. Jika tidak ada arah yang diberikan, maka channel adalah bidirectional (dua arah). Sebuah channel bisa dibatasi hanya untuk mengirim atau menerima saja dengan penempatan atau konversi eksplisit.

chan T          // bisa digunakan untuk mengirim dan menerima nilai bertipe T
chan<- float64  // hanya bisa digunakan mengirim float64
<-chan int      // hanya bisa menerima int

Operator <- berhubungan dengan chan yang paling kiri:

chan<- chan int    // sama dengan chan<- (chan int)
chan<- <-chan int  // sama dengan chan<- (<-chan int)
<-chan <-chan int  // sama dengan <-chan (<-chan int)
chan (<-chan int)

Inisiasi nilai channel yang baru bisa dibuat dengan fungsi bawaan make yang menerima tipe channel dan kapasitas opsional sebagai argumen:

make(chan int, 100)

Kapasitas, atau jumlah elemen, mengatur ukuran buffer dalam channel. Jika kapasitasnya kosong atau tidak ada, channel adalah tanpa buffer dan komunikasi akan sukses saat pengirim dan penerima telah siap. Sebaliknya, bila kapasitas tidak kosong, maka channel adalah ber-buffer dan komunikasi akan sukses tanpa ditahan jika buffer tidak penuh (mengirim) atau tidak kosong (menerima). Channel yang nil tidak akan pernah bisa berkomunikasi.

Channel bisa ditutup dengan fungsi bawaan close. Bentuk penempatan banyak-nilai dari operator penerima melaporkan apakah nilai yang diterima dikirim sebelum channel ditutup.

Sebuah channel bisa digunakan untuk mengirim, menerima, dan memanggil fungsi bawaan cap dan len oleh sejumlah goroutine berbeda tanpa perlu sinkronisasi. Channel bersifat antrean first-in-first-out (yang pertama masuk, yang pertama keluar). Misalnya, jika sebuah goroutine mengirim nilai ke channel dan goroutine kedua menerimanya, nilai diterima sesuai urutan yang dikirim.

Properti dari tipe dan nilai

Identitas tipe

Dua buah tipe akan identik atau berbeda.

Tipe terdefinisi selalu berbeda dengan tipe lainnya. Sebaliknya, dua tipe adalah identik jika tipe dasar mereka secara struktural sama; yaitu, memiliki struktur literal yang sama dan komponen yang berhubungan memiliki tipe yang sama. Secara lebih rinci:

  • Dua tipe array adalah identik jika mereka punya tipe elemen dan panjang yang sama.

  • Dua tipe slice adalah identik jika mereka punya tipe elemen yang sama.

  • Dua tipe struct adalah identik jika mereka memiliki urutan field yang sama, dan jika field-field tersebut memiliki nama. tipe, dan tag yang sama. Nama field yang tidak diekspor dari paket yang berbeda selalu menghasilkan tipe struct yang tidak identik.

  • Dua tipe pointer adalah identik jika mereka memiliki tipe dasar yang sama.

  • Dua tipe fungsi adalah identik jika mereka memiliki jumlah parameter dan kembalian yang sama, dengan tipe parameter dan kembalian yang sama, dan bila kedua fungsi adalah variadic atau tidak sama sekali. Nama pada parameter dan kembalian tidak harus sama.

  • Dua tipe interface adalah identik jika mereka memiliki kumpulan method dengan nama yang sama dan tipe fungsi yang sama. Nama method yang tidak diekspor dari paket yang berbeda akan selalu menghasilkan tipe yang tidak identik. Urutan dari method tidak berpengaruh.

  • Dua tipe map adalah identik jika mereka memiliki tipe key dan elemen yang sama.

  • Dua tipe channel adalah identik jika mereka memiliki tipe elemen dan arah yang sama.

Diberikan deklarasi berikut,

type (
	A0 = []string
	A1 = A0
	A2 = struct{ a, b int }
	A3 = int
	A4 = func(A3, float64) *A0
	A5 = func(x int, _ float64) *[]string
)

type (
	B0 A0
	B1 []string
	B2 struct{ a, b int }
	B3 struct{ a, c int }
	B4 func(int, float64) *B0
	B5 func(x int, y float64) *A1
)

type	C0 = B0

tipe-tipe berikut adalah identik:

A0, A1, dan []string
A2 dan struct{ a, b int }
A3 dan int
A4, func(int, float64) *[]string, dan A5

B0 dan C0
[]int dan []int
struct{ a, b *T5 } dan struct{ a, b *T5 }
func(x int, y float64) *[]string, func(int, float64) (result *[]string), dan A5

B0 dan B1 berbeda karena mereka adalah tipe baru yang dibuat dengan definisi tipe yang berbeda; func(int, float64) *B0 dan func(x int, y float64) *[]string adalah berbeda karena B0 berbeda dari []string.

Assignability (Penempatan)

Sebuah nilai x bisa ditempatkan ke sebuah variabel bertipe T ("x bisa diisi ke T") jika salah satu kondisi berikut berlaku:

  • tipe x identik dengan T.

  • tipe x yaitu V, V dan T memiliki tipe dasar yang sama dan paling tidak salah satu dari V atau T bukanlah tipe tipe terdefinisi.

  • T adalah tipe interface dan x mengimplementasikan T

  • x adalah nilai channel dua arah, T bertipe channel, bila tipe x dari yaitu V dan T memiliki tipe elemen yang sama, dan paling tidak salah satu dari V atau T bukanlah tipe terdefinisi.

  • x adalah nil dan T bertipe pointer, fungsi, slice, map, channel, atau interface.

  • x adalah sebuah konstan direpresentasikan dengan nilai bertipe T.

Representability

Sebuah konstan x bisa direpresentasikan oleh sebuah nilai bertipe T jika salah satu kondisi berikut berlaku:

  • x ada dalam kumpulan nilai yang ditentukan oleh T.

  • T bertipe floating-point dan x bisa dibulatkan ke presisi T tanpa overflow. Pembulatan menggunakan aturan pembulatan-genap dari IEEE 754 namun dengan IEEE nol negatif disederhanakan menjadi unsigned nol. Ingatlah bahwa nilai konstan tidak pernah menghasilkan IEEE nol negatif, NaN, atau tanpa batas.

  • T bertipe complex, dan komponen x real(x) dan imag(x) bisa direpresentasikan oleh nilai tipe komponen dari T (float32 atau float64).

x                   T           x bisa direpresentasikan oleh nilai T karena

'a'                 byte        97 ada dalam kumpulan nilai byte
97                  rune        rune adalah alias untuk int32, dan 97 ada dalam kumpulan integer 32-bit
"foo"               string      "foo" ada dalam kumpulan nilai string
1024                int16       1024 ada dalam kumpulan integer 16-bit
42.0                byte        42 ada dalam kumpulan unsigned integer 8-bit
1e10                uint64      10000000000 ada dalam kumpulan unsigned integer 64-bit
2.718281828459045   float32     2.718281828459045 dibulatkan ke 2.7182817 yang ada dalam kumpulan nilai float32
-1e-1000            float64     -1e-1000 dibulatkan ke IEEE -0.0 yang kemudian disederhanakan menjadi 0.0
0i                  int         0 adalah nilai integer
(42 + 0i)           float32     42.0 (dengan bagian imajiner nol) ada dalam kumpulan nilai float32
x                   T           x tidak direpresentasikan oleh nilai T karena

0                   bool        0 tidak ada dalam kumpulan nilai boolean
'a'                 string      'a' adalah rune, ia tidak ada dalam kumpulan nilai string
1024                byte        1024 bukan berada dalam kumpulan unsigned integer 8-bit
-1                  uint16      -1 bukan berada dalam kumpulan unsigned integer 16-bit
1.1                 int         1.1 bukanlah nilai integer
42i                 float32     (0 + 42i) bukan berada dalam kumpulan nilai float32
1e1000              float64     1e1000 menjadi overflow ke IEEE +Inf setelah pembulatan

Blok

Sebuah blok yaitu seurutan deklarasi dan perintah, yang bisa saja kosong, di antara tanda kurung kurawal.

Block = "{" StatementList "}" .
StatementList = { Statement ";" } .

Selain blok eksplisit dalam kode, ada beberapa blok implisit:

  1. blok universal melingkupi semua teks kode Go

  2. Setiap paket memiliki sebuah blok paket yang berisi semua teks sumber kode Go untuk paket tersebut

  3. Setiap berkas memiliki sebuah blok berkas berisi teks sumber kode Go dalam berkas tersebut

  4. Setiap perintah "if", "for", dan "switch" dianggap berada dalam blok implisit-nya sendiri.

  5. Setiap "case" di dalam perintah "switch" atau "select" bersifat sebagai blok implisit.

Sekumpulan blok yang bersarang mempengaruhi skop.

Deklarasi dan skop

Sebuah deklarasi mengikat pengidentifikasi yang tidak kosong terhadap sebuah konstan, tipe, variabel, fungsi, label, atau paket. Setiap identifikasi dalam sebuah program haruslah dideklarasikan. Tidak ada pengidentifikasi yang bisa dideklarasikan dua kali dalam blok yang sama, dan tidak ada pengidentifikasi bisa dideklarasikan dalam blok berkas dan paket.

Pengidentifikasi kosong bisa digunakan seperti pengidentifikasi lainnya dalam sebuah deklarasi, namun tidak mengakibatkan pengikatan sehingga tidak dideklarasi. Dalam blok paket, pengidentifikasi init hanya bisa digunakan untuk deklarasi fungsi init, dan seperti pengidentifikasi kosong ia tidak menghasilkan pengikatan yang baru.

Declaration   = ConstDecl | TypeDecl | VarDecl .
TopLevelDecl  = Declaration | FunctionDecl | MethodDecl .

Ruang lingkup dari sebuah deklarasi pengidentifikasi yaitu betas dari teks sumber kode di mana pengidentifikasi menyatakan konstan, tipe, variabel, fungsi, label, atau paket yang ditentukan.

Go secara leksikal dibatasi menggunakan blok-blok:

  1. Skop dari pengidentifikasi pra-deklarasi yaitu blok universal.

  2. Skop dari pengidentifikasi yang menyatakan sebuah konstan, tipe, variabel, atau fungsi (tetapi tidak method) yang dideklarasikan pada bagian atas (di luar fungsi apa pun) adalah blok paket.

  3. Skop dari nama paket yang diimpor yaitu blok berkas yang berisi deklarasi impor.

  4. Skop dari pengidentifikasi yang menyatakan penerima method, parameter fungsi, atau variabel kembalian yaitu badan dari fungsi.

  5. Skop dari konstan atau variabel yang dideklarasikan dalam fungsi dimulai dari ConstSpec atau VarSpec (ShortVarDecl untuk deklarasi variabel

    1. dan berakhir pada blok yang mengandungnya.

  6. Skop dari pengidentifikasi tipe yang dideklarasikan dalam sebuah fungsi dimulai dari pengidentifikasi dalam TypeSpec dan berakhir pada blok yang mengandungnya.

Pengidentifikasi yang dideklarasikan dalam sebuah blok bisa dideklarasikan kembali di dalam blok sebelah dalam. Saat pengidentifikasi dari deklarasi di dalam skop, ia menyatakan entitas yang dideklarasikan oleh deklarasi di dalamnya.

Klausa paket bukanlah sebuah deklarasi; nama paket tidak muncul dalam skop manapun. Tujuan klausa paket yaitu untuk mengidentifikasi berkas berada dalam paket yang sama dan untuk menentukan nama paket untuk deklarasi impor.

Skop label

Label dideklarasikan oleh perintah label dan digunakan dalam perintah "break", "continue", dan "goto". Adalah ilegal mendefinisikan sebuah label yang tidak pernah digunakan. Berbeda dengan pengidentifikasi lainnya, label tidaklah dibatasi oleh skip dan tidak konflik dengan pengidentifikasi yang bukan label. Skop dari label yaitu badan dari fungsi di mana ia dideklarasikan dan tidak mengikutkan badan dari fungsi yang bersarang.

Pengidentifikasi kosong

Pengidentifikasi kosong direpresentasikan oleh karakter garis bawah _. Ia berfungsi sebagai penampung anonim bukan sebagai pengidentifikasi biasa (yang bukan kosong) dan memiliki arti khusus dalam deklarasi, seperti sebuah operan, dan dalam penempatan.

Pengidentifikasi pradeklarasi

Pengidentifikasi berikut secara implisit dideklarasikan dalam blok universal:

Tipe:
	bool byte complex64 complex128 error float32 float64
	int int8 int16 int32 int64 rune string
	uint uint8 uint16 uint32 uint64 uintptr

Konstan:
	true false iota

Nilai kosong:
	nil

Fungsi:
	append cap close complex copy delete imag len
	make new panic print println real recover

Pengidentifikasi yang diekspor

Sebuah pengidentifikasi bisa diekspor untuk membolehkan akses kepadanya dari paket lainnya. Pengidentifikasi diekspor jika:

  1. Karakter pertama dari nama pengidentifikasi adalah huruf besar Unicode (kelas Unicode "Lu"); dan

  2. Pengidentifikasi dideklarasikan dalam blok paket atau ia merupakan nama field atau nama method.

Pengidentifikasi lainnya tidak diekspor.

Keunikan pengidentifikasi

Diberikan sekumpulan pengidentifikasi, sebuah pengidentifikasi dikatakan unik jika ia berbeda dari yang lainnya dalam kumpulan tersebut. Dua pengidentifikasi adalah berbeda jika mereka dieja secara berbeda, atau jika mereka muncul di paket yang berbeda dan tidak diekspor. Selain itu, mereka adalah pengidentifikasi yang sama.

Deklarasi konstan

Deklarasi konstan mengikat sejumlah pengidentifikasi (nama-nama dari konstan) terhadap nilai dari daftar dari ekspresi konstan. Jumlah pengidentifikasi harus sama dengan jumlah ekspresi, dan pengidentifikasi ke-n di bagian kiri terikat ke nilai dari ekspresi ke-n di bagian kanan.

ConstDecl      = "const" ( ConstSpec | "(" { ConstSpec ";" } ")" ) .
ConstSpec      = IdentifierList [ [ Type ] "=" ExpressionList ] .

IdentifierList = identifier { "," identifier } .
ExpressionList = Expression { "," Expression } .

Jika tipe didefinisikan, semua konstan memakai tipe tersebut, dan ekspresi nilai haruslah dapat di-assign ke tipe tersebut. Jika tipe diindahkan, maka konstan memiliki tipe berdasarkan ekspresi. Jika nilai ekspresi adalah konstan tanpa tipe, maka konstan tetap tanpa tipe dan pengidentifikasi konstan menyatakan nilai konstan. Misalnya, jika ekspresi adalah literal floating-point, pengidentifikasi konstan menyatakan sebuah konstan floating-point, bahkan bila bagian pecahan adalah nol.

const Pi float64 = 3.14159265358979323846
const zero = 0.0         // konstan floating-point tanpa tipe
const (
	size int64 = 1024
	eof        = -1  // konstan integer tanpa tipe
)
const a, b, c = 3, 4, "foo"  // a = 3, b = 4, c = "foo", konstan integer dan
string tanpa tipe
const u, v float32 = 0, 3    // u = 0.0, v = 3.0

Dalam deklarasi const dengan tanda kurung, daftar ekspresi bisa diindahkan kecuali ConstSpec yang pertama. Daftar kosong seperti ini sama saja dengan penggantian tekstual dari daftar ekspresi pertama yang tidak kosong dan tipenya jika ada. Mengindahkan daftar ekspresi maka sama saja dengan mengulang daftar sebelumnya. Jumlah pengidentifikasi harus sama dengan jumlah ekspresi pada daftar sebelumnya. Bersama dengan iota konstan generator mekanisme ini membolehkan deklarasi ringan dari nilai berurutan:

const (
	Sunday = iota
	Monday
	Tuesday
	Wednesday
	Thursday
	Friday
	Partyday
	numberOfDays  // this constant is not exported
)

Iota

Di dalam sebuah deklarasi konstan, pengidentifikasi iota merepresentasikan konstan integer tanpa-tipe beriringan. Nilainya yaitu indeks dari ConstSpec dalam deklarasi konstan tersebut, dimulai dari nol. Ia bisa digunakan untuk membentuk sekumpulan konstan yang berhubungan:

const (
	c0 = iota  // c0 == 0
	c1 = iota  // c1 == 1
	c2 = iota  // c2 == 2
)

const (
	a = 1 << iota  // a == 1  (iota == 0)
	b = 1 << iota  // b == 2  (iota == 1)
	c = 3          // c == 3  (iota == 2, tidak terpakai)
	d = 1 << iota  // d == 8  (iota == 3)
)

const (
	u         = iota * 42  // u == 0     (konstan integer tanpa tipe)
	v float64 = iota * 42  // v == 42.0  (konstan float64)
	w         = iota * 42  // w == 84    (konstan integer tanpa tipe)
)

const x = iota  // x == 0
const y = iota  // y == 0

Secara definisi, penggunaan iota berulang kali di dalam ConstSpec yang sama memiliki nilai yang sama:

const (
	bit0, mask0 = 1 << iota, 1<<iota - 1  // bit0 == 1, mask0 == 0  (iota == 0)
	bit1, mask1                           // bit1 == 2, mask1 == 1  (iota == 1)
	_, _                                  //                        (iota == 2, tak terpakai)
	bit3, mask3                           // bit3 == 8, mask3 == 7  (iota == 3)
)

Contoh terakhir menggunakan pengulangan implisit dari daftar ekspresi yang tidak kosong.

Deklarasi tipe

Sebuah deklarasi tipe mengikat pengidentifikasi, name tipe, ke sebuah tipe Deklarasi tipe ada dua bentuk: deklarasi alias dan definisi tipe.

TypeDecl = "type" ( TypeSpec | "(" { TypeSpec ";" } ")" ) .
TypeSpec = AliasDecl | TypeDef .

Deklarasi alias

Deklarasi alias mengikat pengidentifikasi ke tipe yang diberikan.

AliasDecl = identifier "=" Type .

Dalam skop pengidentifikasi, ia berfungsi sebagai alias dari tipe.

type (
	nodeList = []*Node  // nodeList dan []*Node adalah tipe yang identik
	Polar    = polar    // Polar dan polar menyatakan tipe yang identik
)

Definisi tipe

Sebuah definisi tipe membuat tipe yang baru dan berbeda dengan tipe dasar dan operasi yang sama dengan tipe yang diberikan, dan mengikat pengidentifikasi padanya.

TypeDef = identifier Type .

Tipe yang baru disebut tipe terdefinisi. Ia berbeda dengan tipe lainnya, termasuk dari tipe yang membentuknya.

type (
	Point struct{ x, y float64 }  // Point dan struct{ x, y float64 }
	                              // adalah tipe yang berbeda.
	polar Point                   // polar dan Point menyatakan tipe yang
	                              // berbeda.
)

type TreeNode struct {
	left, right *TreeNode
	value *Comparable
}

type Block interface {
	BlockSize() int
	Encrypt(src, dst []byte)
	Decrypt(src, dst []byte)
}

Tipe terdefinisi bisa memiliki method yang berasosiasi dengannya. Ia tidak mewariskan method apa pun dari tipe yang diikat, namun kumpulan method dari tipe interface atau elemen dari tipe komposit tidak berubah:

// Mutex adalah tipe data dengan dua method, Lock dan Unlock.
type Mutex struct         { /* field dari Mutex */ }
func (m *Mutex) Lock()    { /* implementasi Lock */ }
func (m *Mutex) Unlock()  { /* implementasi Unlock */ }

// NewMutex memiliki komposisi yang sama dengan Mutex namun set method-nya
// kosong.
type NewMutex Mutex

// Kumpulan method dari tipe dasar PtrMutex yaitu *Mutex tetap tidak berubah,
// namun kumpulan method dari PtrMutex adalah kosong.
type PtrMutex *Mutex

// Kumpulan method dari *PrintableMutex berisi method Lock dan Unlock terikat
// dari field tertanamnya Mutex.
type PrintableMutex struct {
	Mutex
}

// MyBlock yaitu tipe interface yang memiliki kumpulan method yang sama dengan
// Block.
type MyBlock Block

Definisi tipe bisa digunakan untuk mendefinisikan tipe boolean, numerik, atau string yang berbeda dan mengasosiasikan method dengan tipe tersebut:

type TimeZone int

const (
	EST TimeZone = -(5 + iota)
	CST
	MST
	PST
)

func (tz TimeZone) String() string {
	return fmt.Sprintf("GMT%+dh", tz)
}

Deklarasi variabel

Deklarasi variabel membuat satu atau lebih variabel, mengikat pengidentifikasi yang berkorespondensi kepadanya, dan memberikan setiap tiap-tiapnya sebuah tipe dan nilai awal.

VarDecl     = "var" ( VarSpec | "(" { VarSpec ";" } ")" ) .
VarSpec     = IdentifierList ( Type [ "=" ExpressionList ] | "=" ExpressionList ) .
var i int
var U, V, W float64
var k = 0
var x, y float32 = -1, -2
var (
	i       int
	u, v, s = 2.0, 3.0, "bar"
)
var re, im = complexSqrt(-1)
var _, found = entries[name]  // pencarian map; hanya tertarik pada "found"

Jika sebuah daftar ekspresi diberikan, maka variabel diinisiasi dengan ekspresi mengikuti aturan-aturan penempatan. Selain itu, setiap variabel diinisiasi dengan nilai kosong nya.

Jika sebuah tipe diberikan, setiap variabel diberikan tipe tersebut. Selain itu, setiap variabel diberikan tipe dari nilai inisiasi pada penempatan. Jika nilai tersebut sebuah konstan tak bertipe, maka pertama kali ia secara implisit dikonversi ke tipe defaultnya; misalnya, jika variabel adalah nilai boolean tanpa tipe, maka ia secara implisit dikonversi ke tipe bool. Nilai nil tidak bisa digunakan untuk menginisiasi sebuah variabel tanpa tipe eksplisit.

var d = math.Sin(0.5)  // d adalah float64
var i = 42             // i adalah int
var t, ok = x.(T)      // t adalah T, ok adalah bool
var n = nil            // ilegal

Batasan implementasi: Compiler bisa mengilegalkan deklarasi variabel di dalam badan fungsi jika variabel tersebut tidak pernah digunakan.

Deklarasi variabel singkat

Deklarasi variabel singkat menggunakan sintaks:

ShortVarDecl = IdentifierList ":=" ExpressionList .

Ia merupakan cara cepat mendeklarasikan variabel dengan ekspresi inisiasi tanpa tipe:

"var" IdentifierList = ExpressionList .
i, j := 0, 10
f := func() int { return 7 }
ch := make(chan int)
r, w, _ := os.Pipe()  // os.Pipe() mengembalikan pasangan File dan error
_, y, _ := coord(p)   // coord() mengembalikan tiga nilai; yang diambil hanya koordinat y

Tidak seperti deklarasi variabel, deklarasi variabel singkat bisa mendeklarasi ulang variabel setelah ia dideklarasikan sebelumnya di dalam blok yang sama (atau dari daftar parameter jika blok adalah badan fungsi) dengan tipe yang sama, dan paling tidak salah satu dari variabel yang tidak kosong adalah variabel baru. Akibatnya, deklarasi ulang hanya dapat muncul dalam sebuah deklarasi singkat multi-variabel. Deklarasi ulang tidak menyebabkan munculnya variabel baru; ia hanya menempatkan nilai baru ke variabel aslinya.

field1, offset := nextField(str, 0)
field2, offset := nextField(str, offset)  // deklarasi ulang pada offset
a, a := 1, 2                              // ilegal: deklarasi ganda dari a
                                          // atau tidak ada variabel baru bila
					  // a dideklarasikan sebelumnya.

Deklarasi variabel singkat hanya bisa muncul dalam fungsi. Dalam beberapa konteks seperti inisiasi untuk perintah "if", "for", atau "switch", mereka dapat digunakan untuk mendeklarasikan variabel lokal sementara.

Deklarasi fungsi

Deklarasi fungsi mengikat pengidentifikasi, nama fungsi, ke sebuah fungsi.

FunctionDecl = "func" FunctionName Signature [ FunctionBody ] .
FunctionName = identifier .
FunctionBody = Block .

Jika fungsi mengembalikan nilai, daftar perintah pada badan fungsi harus berakhir dengan perintah terminasi.

func IndexRune(s string, r rune) int {
	for i, c := range s {
		if c == r {
			return i
		}
	}
	// tidak valid: perintah return tidak ada.
}

Deklarasi fungsi bisa tanpa badan. Deklarasi seperti ini menyediakan signature untuk sebuah fungsi yang diimplementasikan di luar Go, seperti rutin assembly.

func min(x int, y int) int {
	if x < y {
		return x
	}
	return y
}

func flushICache(begin, end uintptr)  // diimplementasikan di luar.

Deklarasi method

Sebuah method yaitu sebuah fungsi dengan sebuah receiver (penerima). Deklarasi method mengikat pengidentifikasi, nama method, terhadap sebuah method, dan mengasosiasikan method tersebut dengan tipe dasar receiver.

MethodDecl = "func" Receiver MethodName Signature [ FunctionBody ] .
Receiver   = Parameters .

Si receiver dispesifikasikan lewat bagian parameter sebelum nama method. Bagian parameter tersebut harus mendeklarasikan sebuah parameter tunggal, yang disebut juga dengan receiver. Tipe receiver haruslah tipe terdefinisi T atau sebuah pointer ke tipe terdefinisi T. T disebut juga tipe dasar receiver. Tipe dasar receiver tidak bisa berupa tipe pointer atau interface dan ia harus didefinisikan di dalam paket yang sama dengan method. Method tersebut dikatakan terikat dengan tipe dasar receiver dan nama method hanya dapat dipanggil oleh selector untuk tipe T atau *T.

Pengidentifikasi receiver haruslah unik dalam penanda method. Jika nilai receiver tidak dipakai di dalam badan method, maka pengidentifikasinya bisa dihilangkan dalam deklarasi. Hal yang sama berlaku secara umum terhadap parameter dari fungsi dan method.

Untuk tipe dasar yang sama, nama-nama dari method haruslah unik. Jika tipe dasar adalah sebuah tipe struct, nama method dan field haruslah berbeda.

Diberikan sebuah tipe Point, deklarasi berikut

func (p *Point) Length() float64 {
	return math.Sqrt(p.x * p.x + p.y * p.y)
}

func (p *Point) Scale(factor float64) {
	p.x *= factor
	p.y *= factor
}

mengikat method Length() dan Scale(), dengan receiver bertipe *Point, terhadap tipe dasar Point.

Tipe dari sebuah method yaitu tipe dari fungsi dengan receiver sebagai argumen yang pertama. Misalnya, method Scale() bertipe

func(p *Point, factor float64)

Namun, fungsi yang dideklarasikan seperti di atas bukanlah sebuah method.

Ekspresi

Sebuah ekspresi menentukan komputasi dari sebuah nilai dengan menerapkan operator dan fungsi terhadap operan.

#Operands === Operan-operan

Operan menyatakan nilai elementer dalam sebuah ekspresi. Sebuah operan bisa jadi literal, sebuah pengindentifikasi bukan- kosong (bisa saja /ref/spec#Qualified_identifiers[terbatas]) yang menyatakan sebuah konstan, variabel, atau fungsi, atau ekspresi dalam tanda kurung.

Pengidentifikasi kosong bisa muncul sebaga operan hanya pada bagian kiri dari sebuah penempatan.

Operand     = Literal | OperandName | "(" Expression ")" .
Literal     = BasicLit | CompositeLit | FunctionLit .
BasicLit    = int_lit | float_lit | imaginary_lit | rune_lit | string_lit .
OperandName = identifier | QualifiedIdent.

Pengidentifikasi terbatas

Sebuah pengidentifikasi terbatas yaitu sebuah pengidentifikasi yang dibatasi oleh awalan nama paket. Nama paket dan pengidentifikasi haruslah tidak kosong.

QualifiedIdent = PackageName "." identifier .

Pengidentifikasi terbatas mengakses pengidentifikasi di paket yang berbeda, yang harus diimpor. Si pengidentifikasi haruslah diekspor dan dideklarasikan dalam blok paket dari paket tersebut.

math.Sin	// menyatakan fungsi Sin dalam paket math.

Literal komposit

Literal komposit membentuk nilai-nilai untuk struct, array, slice, dan map; dan membuat sebuah nilai baru setiap kali ia dievaluasi. Ia dibentuk dari tipe dari literal diikuti oleh daftar elemen yang dibatasi oleh kurung kurawal. Setiap elemen bisa diawali dengan kunci yang berkorespondensi.

CompositeLit  = LiteralType LiteralValue .
LiteralType   = StructType | ArrayType | "[" "..." "]" ElementType |
                SliceType | MapType | TypeName .
LiteralValue  = "{" [ ElementList [ "," ] ] "}" .
ElementList   = KeyedElement { "," KeyedElement } .
KeyedElement  = [ Key ":" ] Element .
Key           = FieldName | Expression | LiteralValue .
FieldName     = identifier .
Element       = Expression | LiteralValue .

Tipe dasar dari LiteralType haruslah sebuah tipe struct, array, slice, atau map (gramatika memaksa batasan ini kecuali bisa tipe diberikan sebagai sebuah TypeName). Tipe dari elemen dan kunci harus bisa ditempatkan ke tipe field, elemen, dan kunci dari tipe literal yang bersangkutan; tidak ada konversi tambahan. Kunci diinterpretasikan sebagai sebuah nama field untuk literal struct, sebuah indeks pada literal array dan slice, dan sebuah kunci untuk literal map. Untuk literal map, semua elemen harus memiliki sebuah kunci. Adalah sebuah kesalahan bila menspesifikasikan beragam lemen dengan nama field yang sama atau nilai key menggunakan konstan. Untuk kunci map yang tidak-konstan, lihat bagian pada urutan evaluasi.

Untuk literal struct aturan-aturan berikut berlaku:

  • Sebuah kunci haruslah nama field yang dideklarasikan dalam tipe struct.

  • Daftar elemen yang tidak memiliki kunci haruslah mendaftarkan setiap elemen pada field struct dengan urutan sebagaimana ia dideklarasikan.

  • Jika elemen memiliki key, maka semua elemen haruslah memiliki key.

  • Daftar elemen yang mengandung kunci tidak harus memiliki elemen untuk setiap field struct. Field yang diindahkan akan mendapatkan nilai kosong untuk field tersebut.

  • Literal bisa mengindahkan daftar elemen; literal tersebut dievaluasi menjadi nilai kosong untuk tipenya.

  • Adalah sebuah kesalahan bila menspesifikasikan sebuah elemen untuk field yang tidak diekspor dari sebuah struct yang dimiliki oleh paket yang berbeda.

Diberikan deklarasi berikut

type Point3D struct { x, y, z float64 }
type Line struct { p, q Point3D }

kita bisa menulis

origin := Point3D{}                            // nilai kosong untuk Point3D
line := Line{origin, Point3D{y: -4, z: 12.3}}  // nilai kosong untuk line.q.x

Untuk array dan slice, aturan-aturan berikut berlaku:

  • Setiap elemen memiliki indeks integer yang menandakan posisinya dalam array.

  • Sebuah elemen dengan sebuah kunci menggunakan kunci tersebut sebagai indeksnya. Kunci tersebut haruslah konstan bukan-negatif yang dapat direpresentasikan oleh nilai bertipe int; dan jika kunci tersebut bertipe maka harus bertipe integer.

  • Sebuah elemen tanpa kunci menggunakan indeks elemen sebelumnya ditambah satu. Jika elemen pertama tanpa kunci, indeksnya adalah nol.

Mengambil alamat dari literal komposit menghasilkan sebuah pointer ke sebuah variabel unik yang diinisiasi dengan nilai literal.

var pointer *Point3D = &Point3D{y: 1000}

Ingatlah bahwa nilai kosong dari sebuah tipe slice atau map tidak sama dengan slice atau yang map diinisiasi dengan nilai kosong dari tipe yang sama. Akibatnya, mengambil alamat dari literal komposit dari sebuah slice atau map yang kosong tidak sama efeknya dengan mengalokasikan slice atau map yang baru dengan new.

p1 := &[]int{}    // p1 menunjuk ke slice kosong yang diinisiasi dengan nilai
                  // []int{} dan panjang 0
p2 := new([]int)  // p2 menunjuk ke slice yang belum diinisiasi dengan nilai
                  // nil dan panjang 0.

Panjang dari literal array yaitu panjang yang dispesifikasikan pada literal tipe. Jika jumlah elemen yang diberikan kecil dari panjangnya, elemen-elemen yang hilang di set dengan nilai kosong dari tipe elemen array. Adalah sebuah kesalahan mengisi elemen dengan nilai indeks di luar rentang indeks dari array. Notasi …​ menspesifikasikan panjang array yang sama dengan maksimum elemen ditambah satu.

buffer := [10]string{}             // len(buffer) == 10
intSet := [6]int{1, 2, 3, 5}       // len(intSet) == 6
days := [...]string{"Sat", "Sun"}  // len(days) == 2

Literal slice mendeskripsikan keseluruhan literal array di belakangnya. Maka panjang dan kapasitas dari literal slice yaitu indeks elemen maksimum ditambah satu. Literal slice memiliki bentuk

[]T{x1, x2, … xn}

yang merupakan cara singkat untuk operasi slice yang diterapkan pada sebuah array:

tmp := [n]T{x1, x2, … xn}
tmp[0 : n]

Dalam sebuah literal komposit dari array, slice, atau map bertipe T, elemen atau kunci map yang juga literal komposit bisa mengindahkan literal tipe jika ia identik dengan tipe elemen atau key dari T. Hal yang sama, elemen atau kunci yang merupakan alamat dari literal komposit bisa mengindahkan &T bila tipe elemen atau kunci adalah *T.

[...]Point{{1.5, -3.5}, {0, 0}}     // sama dengan [...]Point{Point{1.5, -3.5}, Point{0, 0}}
[][]int{{1, 2, 3}, {4, 5}}          // sama dengan [][]int{[]int{1, 2, 3}, []int{4, 5}}
[][]Point{{{0, 1}, {1, 2}}}         // sama dengan [][]Point{[]Point{Point{0, 1}, Point{1, 2}}}
map[string]Point{"orig": {0, 0}}    // sama dengan map[string]Point{"orig": Point{0, 0}}
map[Point]string{{0, 0}: "orig"}    // sama dengan map[Point]string{Point{0, 0}: "orig"}

type PPoint *Point
[2]*Point{{1.5, -3.5}, {}}          // sama dengan [2]*Point{&Point{1.5, -3.5}, &Point{}}
[2]PPoint{{1.5, -3.5}, {}}          // sama dengan [2]PPoint{PPoint(&Point{1.5, -3.5}), PPoint(&Point{})}

Ambiguitas muncul saat literal komposit menggunakan bentuk TypeName dari LiteralType muncul sebagai operan antara kata kunci dan kurung buka dari blok perintah "if", "for", atau "switch", dan literal komposit tidak ditutup dalam tanda kurung, kurung siku, atau kurung kurawal. Dalam kasus langka ini, kurung buka dari literal dengan keliru dianggap sebagai yang membuka perintah blok. Untuk mengatasi kebingungan ini, literal komposit harus muncul dalam tanda kurung.

if x == (T{a,b,c}[i]) { … }
if (x == T{a,b,c}[i]) { … }

Contoh literal array, slice, dan map yang valid:

// daftar bilangan prima
primes := []int{2, 3, 5, 7, 9, 2147483647}

// vowels[ch] adalah true jika ch adalah huruf vokal.
vowels := [128]bool{'a': true, 'e': true, 'i': true, 'o': true, 'u': true, 'y': true}

// array [10]float32{-1, 0, 0, 0, -0.1, -0.1, 0, 0, 0, -1}
filter := [10]float32{-1, 4: -0.1, -0.1, 9: -1}

// frekuensi dalam Hz untuk skala yang sama (A4 = 440Hz)
noteFrequency := map[string]float32{
	"C0": 16.35, "D0": 18.35, "E0": 20.60, "F0": 21.83,
	"G0": 24.50, "A0": 27.50, "B0": 30.87,
}

Literal fungsi

Sebuah literal fungsi merepresentasikan sebuah fungsi anonim.

FunctionLit = "func" Signature FunctionBody .
func(a, b int, z float64) bool { return a*b < int(z) }

Sebuah literal fungsi bisa ditempatkan ke sebuah variabel atau langsung dipanggil.

f := func(x, y int) int { return x + y }
func(ch chan int) { ch <- ACK }(replyChan)

Literal fungsi adalah closure: fungsi yang bisa mengacu ke variabel-variabel yang didefinisikan sebelum fungsi. Variabel tersebut dapat diakses antara fungsi dan literal fungsi, dan variabel tersebut bertahan selama mereka dapat diakses.

Ekspresi dasar

Ekspresi dasar yaitu operan-operan untuk ekspresi unary dan binari.

PrimaryExpr =
	Operand |
	Conversion |
	MethodExpr |
	PrimaryExpr Selector |
	PrimaryExpr Index |
	PrimaryExpr Slice |
	PrimaryExpr TypeAssertion |
	PrimaryExpr Arguments .

Selector       = "." identifier .
Index          = "[" Expression "]" .
Slice          = "[" [ Expression ] ":" [ Expression ] "]" |
                 "[" [ Expression ] ":" Expression ":" Expression "]" .
TypeAssertion  = "." "(" Type ")" .
Arguments      = "(" [ ( ExpressionList | Type [ "," ExpressionList ] ) [ "..." ] [ "," ] ] ")" .
x
2
(s + ".txt")
f(3.1415, true)
Point{1, 2}
m["foo"]
s[i : j + 1]
obj.color
f.p[i].x()

Selector

Untuk sebuah ekspresi dasar x yang bukan sebuah nama paket, ekspresi selektor

x.f

menyatakan field atau method f dari nilai x (atau terkadang *x; lihat di bawah). Pengidentifikasi f disebut (field atau method) selector; ia tidak boleh sebuah pengidentifikasi kosong. Tipe dari ekspresi selector yaitu tipe dari f. Jika x adalah sebuah nama paket, lihat bagian pengidentifikasi terbatas.

Sebuah selector f bisa menyatakan sebuah field atau method f dari sebuah tipe T, atau ia bisa mengacu pada sebuah field atau method f dari field tertanam dari T. Jumlah field tertanam yang dijajaki sampai ke f disebut dengan kedalaman dari T. Kedalaman dari sebuah field atau method f yang dideklarasikan dalam T adalah nol. Kedalaman dari sebuah field atau method f yang dideklarasikan dalam field A yang ditanam dalam T adalah kedalaman f dalam A ditambah satu.

Aturan-aturan berikut berlaku pada selector:

  1. Untuk nilai x bertipe T atau *T yang mana T bukanlah sebuah tipe pointer atau interface, x.f menyatakan field atau method pada kedalaman paling dangkal dalam T yang mana f tersebut ada. Jika tidak ada satu f pada kedalaman terdangkal, ekspresi selector tersebut adalah ilegal.

  2. Untuk sebuah nilai x bertipe I yang mana I adalah sebuah tipe interface, x.f menyatakan method bernama f dari nilai dinamis x. Jika tidak ada method bernama f dalam kumpulan method dari I, ekspresi selector tersebut adalah ilegal.

  3. Sebagai pengecualian, jika tipe dari x merupakan tipe pointer terdefinisi dan (*x).f adalah ekspresi selector yang valid yang menyatakan sebuah field (bukan sebuah method), x.f adalah singkatan untuk (*x).f.

  4. Pada kasus lainnya x.f adalah ilegal.

  5. Jika x adalah tipe pointer dan bernilai nil dan x.f menyatakan sebuah field pada struct, mengisi atau mengambil nilai x.f mengakibatkan panik runtime.

  6. Jika x bertipe interface dan bernilai nil, memanggil atau mengevaluasi method x.f menyebakan panik runtime.

Sebagai contoh, dari deklarasi berikut:

type T0 struct {
	x int
}

func (*T0) M0()

type T1 struct {
	y int
}

func (T1) M1()

type T2 struct {
	z int
	T1
	*T0
}

func (*T2) M2()

type Q *T2

var t T2     // dengan t.T0 != nil
var p *T2    // dengan p != nil dan (*p).T0 != nil
var q Q = p

kita dapat menulis:

t.z          // t.z
t.y          // t.T1.y
t.x          // (*t.T0).x

p.z          // (*p).z
p.y          // (*p).T1.y
p.x          // (*(*p).T0).x

q.x          // (*(*q).T0).x        (*q).x adalah selector field yang valid

p.M0()       // ((*p).T0).M0()      M0 mendapatkan penerima *T0
p.M1()       // ((*p).T1).M1()      M1 mendapatkan penerima T1
p.M2()       // p.M2()              M2 mendapatkan penerima *T2
t.M2()       // (&t).M2()           M2 mendapatkan penerima *T2, lihat bagian tentang Pemanggilan

namun pernyataan berikut adalah invalid:

q.M0()       // (*q).M0 valid namun bukan sebuah selector field

[#Method_expressions === Ekspresi method

Jika M ada dalam kumpulan method bertipe T, T.M adalah sebuah fungsi yang dapat dipanggil seperti fungsi biasa dengan argumen yang sama dengan M yang diawali dengan argumen tambahan yaitu penerima (receiver) dari method.

MethodExpr    = ReceiverType "." MethodName .
ReceiverType  = Type .

Misalkan sebuah struct bertipe T dengan dua method, Mv, dengan penerima bertipe T; dan Mp yang penerimanya bertipe *T.

type T struct {
	a int
}
func (tv  T) Mv(a int) int         { return 0 }  // penerima nilai
func (tp *T) Mp(f float32) float32 { return 1 }  // penerima pointer

var t T

Ekspresi

T.Mv

menghasilkan sebuah fungsi yang sama dengan Mv namun dengan penerima eksplisit sebagai argumen pertama; dengan penanda

func(tv T, a int) int

Fungsi tersebut bisa dipanggil secara normal dengan penerima eksplisit, sehingga kelima pemanggilan berikut adalah sama:

t.Mv(7)
T.Mv(t, 7)
(T).Mv(t, 7)
f1 := T.Mv; f1(t, 7)
f2 := (T).Mv; f2(t, 7)

Hal yang sama, ekspresi berikut

(*T).Mp

menghasilkan sebuah nilai fungsi yang merepresentasikan Mp dengan penanda

func(tp *T, f float32) float32

Untuk sebuah method dengan penerima nilai, kita dapat menurunkan sebuah fungsi dengan penerima pointer yang eksplisit, sehingga

(*T).Mv

menghasilkan sebuah nilai fungsi merepresentasikan Mv dengan penanda

func(tv *T, a int) int