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

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 (x_nn) 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 | InterfaceTypeName ) ";" } "}" .
MethodSpec         = MethodName Signature .
MethodName         = identifier .
InterfaceTypeName  = TypeName .

Sebuah tipe interface bisa menspesifikasikan beberapa method secara eksplisit lewat spesifikasi method, atau ia bisa menanam method-method dari interface yang lain lewat nama tipe interface.

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

Nama dari setiap method yang secara eksplisit dispesifikasikan haruslah unik dan tidak kosong.

interface {
	String() string
	String() string  // ilegal: String tidak unik
	_(x int)         // ilegal: nama method tidak boleh kosong
}

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
func (p T) Write(b Buffer) bool
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. Kumpulan method dari T adalah gabungan dari kumpulan method T yang secara eksplisit dan interface-interface yang tertanam pada T.

type Reader interface {
	Read(p []byte) (n int, err error)
	Close() error
}

type Writer interface {
	Write(p []byte) (n int, err error)
	Close() error
}

// ReadWriter's methods are Read, Write, and Close.
type ReadWriter interface {
	Reader  // includes methods of Reader in ReadWriter's method set
	Writer  // includes methods of Writer in ReadWriter's method set
}

Gabungan dari kumpulan method berisi method-method (yang diekspor atau tidak diekspor) yang mana setiap method di set sekali saja, dan method-method dengan nama yang sama haruslah memiliki penanda yang identik.

type ReadCloser interface {
	Reader   // mengikutkan method-method Reader dalam ReadCloser
	Close()  // ilegal: penanda dari Reader.Close dan Close berbeda
}

Interface bertipe T tidak bisa secara rekursif 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 singkat) 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 <<Qualified_identifiers[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 receiver *T0
p.M1()       // ((*p).T1).M1()      M1 mendapatkan receiver T1
p.M2()       // p.M2()              M2 mendapatkan receiver *T2
t.M2()       // (&t).M2()           M2 mendapatkan receiver *T2, lihat bagian tentang Pemanggilan

namun pernyataan berikut adalah invalid:

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

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 receiver dari method.

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

Misalkan sebuah struct bertipe T dengan dua method, Mv, dengan receiver bertipe T; dan Mp dengan receiver bertipe *T.

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

var t T

Ekspresi

T.Mv

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

func(tv T, a int) int

Fungsi tersebut bisa dipanggil secara normal dengan receiver 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 receiver nilai, kita dapat menurunkan sebuah fungsi dengan receiver pointer yang eksplisit, sehingga

(*T).Mv

menghasilkan sebuah nilai fungsi merepresentasikan Mv dengan penanda

func(tv *T, a int) int

Fungsi seperti ini secara tidak langsung lewat receiver untuk membuat sebuah nilai untuk dikirim sebagai receiver pada method dibaliknya; method tersebut tidak menimpa nilai yang alamatnya dikirim ke pemanggilan fungsi.

Kasus terakhir, fungsi dengan receiver berupa nilai untuk sebuah method dengan receiver berupa pointer, adalah ilegal karena method-method dengan receiver pointer tidak berada dalam kumpulan method dari tipe nilai.

Nilai-nilai fungsi yang diturunkan dari method dipanggil dengan sintaksis pemanggilan fungsi biasa; si receiver diberikan sebagai argumen pertama pada pemanggilan. Maka, diberikan f := T.Mv, f dipanggil dengan cara f(t,7) bukan t.f(7). Untuk membentuk sebuah fungsi yang mengikat receiver, gunakan literal fungsi atau nilai method.

Adalah legal menurunkan sebuah nilai fungsi dari sebuah method bertipe interface. Hasil fungsinya menerima receiver eksplisit dari tipe interface tersebut.

Nilai method

Jika ekspresi x memiliki tipe statis T dan M berada dalam kumpulan method dari tipe T, x.M disebut nilai method. Nilai method x.M yaitu sebuah nilai fungsi yang dapat dipanggil dengan argumen yang sama seperti memanggil method dari x.M. Ekspresi x dievaluasi dan disimpan selama evaluasi dari nilai method; salinan yang disimpan kemudian digunakan sebagai receiver pada setiap pemanggilan, yang bisa saja dieksekusi nantinya.

Tipe T bisa berupa tipe interface atau bukan interface.

Seperti yang telah didiskusikan dalam ekspresi method di atas, misalnya ada sebuah struct bertipe T dengan dua method, Mv, dengan receiver bertipe T, dan Mp, dengan receiver bertipe *T.

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

var t T
var pt *T
func makeT() T

Ekspresi dari

t.Mv

menghasilkan sebuah nilai fungsi bertipe

func(int) int

Pemanggilan berikut adalah sama:

t.Mv(7)
f := t.Mv; f(7)

Hal yang sama, ekspresi berikut

pt.Mp

menghasilkan sebuah nilai fungsi bertip

func(float32) float32

Seperti halnya dengan selector, sebuah referensi ke method yang bukan interface dengan receiver nilai menggunakan pointer akan secara otomatis membalikan pointer tersebut: pt.Mv sama dengan (*pt)Mv.

Seperti halnya dengan pemanggilan method, sebuah referensi ke method yang bukan interface dengan receiver pointer menggunakan nilai beralamat akan secara otomatis mengambil alamat dari nilai tersebut: t.Mp sama dengan (&t).Mp.

f := t.Mv; f(7)   // sama dengan t.Mv(7)
f := pt.Mp; f(7)  // sama dengan pt.Mp(7)
f := pt.Mv; f(7)  // sama dengan (*pt).Mv(7)
f := t.Mp; f(7)   // sama dengan (&t).Mp(7)
f := makeT().Mp   // tidak valid: hasil dari makeT() tidak beralamat.

Walaupun contoh di atas menggunakan tipe bukan interface, merupakan hal yang legal untuk membuat sebuah nilai method dari nilai bertipe interface.

var i interface { M(int) } = myVal
f := i.M; f(7)  // sama dengan i.M(7)

Ekspresi indeks

Ekspresi dasar dari bentuk

a[x]

menyatakan elemen dari array, pointer ke array, slice, atau map a yang diindeks oleh x. Nilai x disebut dengan indeks atau map key. Aturan-aturan berikut berlaku:

Jika a bukan sebuah map:

  • indeks x haruslah bertipe integer atau konstanta tanpa tipe

  • indeks berupa konstanta haruslah tidak negatif dan dapat direpresentasikan oleh sebuah nilai bertipe int

  • indeks berupa konstanta yang tidak bertipe akan diberikan tipe int

  • indeks x berada dalam rentang jika 0 ⇐ x < len(a), selain itu di luar rentang

Untuk a dengan tipe Array A:

  • sebuah indeks berupa konstanta haruslah dalam rentang

  • jika x di luar rentang pada saat runtime, sebuah panik runtime akan terjadi

  • a[x] adalah elemen array pada indeks x dan tipe dari a[x] yaitu elemen bertipe A

Untuk a sebagai pointer ke tipe array:

  • a[x] adalah singkatan untuk (*a)[x]

Untuk a dengan tipe slice S:

  • jika x di luar rentang pada saat runtime, panik runtime akan terjadi

  • a[x] adalah elemen slice pada indeks x dan tipe dari a[x] yaitu elemen bertipe S

Untuk a dari tipe string:

  • sebuah indeks berupa konstanta haruslah dalam rentang jika string a juga konstanta`

  • jika x di luar rentang pada saat runtime, panik runtime akan terjadi

  • a[x] adalah nilai byte bukan konstanta pada indeks x dan tipe dari a[x] adalah byte

  • a[x] tidak bisa ditimpa bila a adalah konstanta

Untuk a dari tipe map M:

  • tipe x haruslah bisa disimpan ke tipe key dari M

  • jika map berisi sebuah nilai dengan key x, a[x] adalah elemen map dengan key x dan tipe dari a[x] yaitu tipe elemen dari M

  • jika map adalah nil atau tidak mengandung nilai dengan key x, a[x] yaitu nilai kosong dari tipe elemen dari M.

Selain itu a[x] adalah ilegal.

Ekspresi indeks dapat sebuah map bertipe map[K]V yang digunakan dalam sebuah penempatan atau inisiasi dari bentuk khusus

v, ok = a[x]
v, ok := a[x]
var v, ok = a[x]

menghasilkan sebuah nilai boolean tidak bertipe tambahan. Nilai dari ok adalah true jika key x ada dalam map, dan false jika tidak ada.

Menempatkan ke elemen map yang nil akan mengakibatkan panik runtime.

Ekspresi pemotongan

Ekspresi pemotongan membentuk sebuah substring atau slice dari sebuah string, array, pointer ke array, atau slice. Ada dua varian: bentuk sederhana yang menentukan batas bawah dan atas, dan bentuk penuh yang juga menentukan batas dari kapasitas.

Ekspresi sederhana pemotongan

Untuk sebuah string, array, pointer ke array, atau slice a, ekspresi dasar

a[low : high]

membentuk sebuah substring atau slice. Indeks low dan high memilih elemen mana dari operan a yang akan dikembalikan. Hasil kembaliannya akan memiliki indeks yang berawal dari 0 dan panjang sama dengan high - low. Setelah memotong array a

a := [5]int{1, 2, 3, 4, 5}
s := a[1:4]

slice s bertipe []int, panjang 3, kapasitas 4, dan elemennya

s[0] == 2
s[1] == 3
s[2] == 4

Demi kemudahan, indeks-indeks tersebut bisa diindahkan. Nilai indeks low yang diindahkan yaitu nol; Nilai indeks high yang diindahkan yaitu panjang dari operan slice:

a[2:]  // sama dengan a[2 : len(a)]
a[:3]  // sama dengan a[0 : 3]
a[:]   // sama dengan a[0 : len(a)]

Jika a adalah pointer ke sebuah array, a[low : high] adalah singkatan untuk (*a)[low : high].

Untuk array atau string, indeks berada dalam rentang jika 0 ⇐ low ⇐ high ⇐ len(a), selain itu berarti di luar rentang. Untuk slice, batas atas dari indeks yaitu kapasitas slice cap(a) bukan panjangnya. Indeks berupa konstanta haruslah tidak negatif dan dapat direpresentasikan oleh nilai bertipe int; untuk array atau string konstanta, indeks dari konstanta haruslah berada dalam rentang. Jika kedua indeks adalah konstanta, keduanya haruslah memenuhi low ⇐ high. Jika indeks-indeks di luar rentang pada saat runtime, panik runtime terjadi.

Kecuali untuk string tak bertipe, jika operan pemotongan adalah sebuah string atau slice, hasil dari operasi pemotongan adalah nilai non-konstanta bertipe string. Jika opera pemotongan adalah sebuah array, maka harus dapat memiliki alamat dan hasil dari operasi pemotongan yaitu sebuah slice dengan tipe elemen yang sama dengan array.

Jika operan slice dari sebuah ekspresi pemotongan yang valid adalah sebuah slice yang nil, hasilnya yaitu sebuah slice nil. Selain itu, jika hasilnya adalah sebuah slice, ia akan memiliki array dasar yang sama dengan operan.

var a [10]int
s1 := a[3:7]   // array dasar dari s1 yaitu array a; &s1[2] == &a[5]
s2 := s1[1:4]  // array dasar dari s2 yaitu array dasar dari s1 yaitu array a;
               // &s2[1] == &a[5]
s2[1] = 42     // s2[1] == s1[2] == a[5] == 42; semuanya mengacu pada elemen
               // array dasar yang sama

Ekspresi pemotongan penuh

Untuk sebuah array, pointer ke array, atau slice a (tetapi bukan string), ekspresi dasar

a[low : high : max]

membentuk sebuah slice dengan tipe yang sama, dan dengan panjang dan elemen seperti pada ekspresi pemotongan sederhana a[low : high]. Sebagai tambahan, ia mengontrol kapasitas slice kembalian dengan mensetnya menjadi max - low. Hanya indeks pertama yang boleh diindahkan; yang bernilai dasar 0. Setelah memotong array a

a := [5]int{1, 2, 3, 4, 5}
t := a[1:3:5]

slice t bertipe []int, panjang 2, kapasitas 4, dan elemennya

t[0] == 2
t[1] == 3

Seperti pada ekspresi pemotongan sederhana, jika a adalah sebuah pointer ke array, a[low : high : max] adalah singkatan dari (*a)[low : high :max]. Jika operan pemotongan adalah sebuah array, maka haruslah memiliki alamat.

Indeks-indeksnya berada dalam rentang jika 0 ⇐ low ⇐ high ⇐ max ⇐ cap(a), selain itu adalah di luar rentang. Indeks berupa konstanta haruslah tidak negatif dan direpresentasikan oleh sebuah nilai bertipe int; untuk array, indeks konstanta haruslah juga berada dalam rentang. Jika beberapa indeks adalah konstanta, konstanta tersebut haruslah dalam rentang yang relatif terhadap satu sama lain. Jika indeks-indeks di luar rentang pada saat runtime, panik runtime terjadi.

Asersi Tipe

Untuk sebuah ekspresi x dari tipe interface dan sebuah tipe T, ekspresi dasar berikut

x.(T)

menyatakan bahwa x tidak nil dan nilai yang tersimpan dalam x bertipe T. Notasi x.(T) disebut dengan asersi tipe.

Lebih tepatnya, jika T bukan bertipe interface, x.(T) menyatakan bahwa tipe dinamis dari x adalah identik dengan tipe T. Dalam kasus ini, T harus mengimplementasikan tipe (interface) dari x; sebaliknya asersi tipe tidak valid karena tidak mungkin bagi x untuk menyimpan nilai bertipe T. Jika T adalah tipe interface, x.(T) menyatakan bahwa tipe dinamis dari x mengimplementasikan interface T.

Jika asersi tipe berhasil, nilai dari ekspresi tersebut yaitu nilai yang tersimpan dalam x dan tipenya yaitu T. Jika asersi tipe gagal, panik runtime terjadi. Dengan kata lain, walaupun tipe dinamis dari x diketahui pada saat runtime, tipe dari x.(T) hanya diketahui sebagai T dalam program yang benar.

var x interface{} = 7          // x bertipe dinamis int dan nilainya 7
i := x.(int)                   // i bertipe int dan bernilai 7

type I interface { m() }

func f(y I) {
	s := y.(string)        // ilegal: string tidak mengimplementasikan
	                       // I (method m tidak ada)
	r := y.(io.Reader)     // r bertipe io.Reader dan tipe dinamis dari y
	                       // harus mengimplementasikan I dan io.Reader
	…
}

Asersi tipe yang digunakan dalam penempatan atau inisiasi dari bentuk khusus

v, ok = x.(T)
v, ok := x.(T)
var v, ok = x.(T)
var v, ok T1 = x.(T)

menghasilkan nilai boolean tambahan. Nilai dari ok adalah true jika asersi berhasil. Sebaliknya ia akan false dan nilai dari v adalah nilai kosong untuk tipe T. Tidak ada panik _runtime yang akan terjadi dalam kasus ini.

Pemanggilan

Diberikan sebuah ekspresi f dari tipe fungsi F,

f(a1, a2, … an)

akan memanggil f dengan argumen a1, a2, …​ an. Kecuali untuk kasus khusus, argumen-argumen haruslah ekspresi nilai-tunggal yang dapat disimpan ke tipe parameter dari F dan dievaluasi sebelum fungsi dipanggil. Tipe dari ekspresi adalah tipe kembalian dari F. Pemanggilan sebuah method mirip namun method itu sendiri ditentukan sebagai sebuah selector dari sebuah nilai dari tipe receiver untuk method tersebut.

math.Atan2(x, y)  // pemanggilan fungsi
var pt *Point
pt.Scale(3.5)     // pemanggilan method dengan receiver pt

Dalam pemanggilan sebuah fungsi, nilai fungsi dan argumennya dievaluasi dengan urutan yang biasa. Setelah mereka dievaluasi, parameter-parameter dari pemanggilan dikirim dengan nilai ke dalam fungsi dan fungsi yang dipanggil tersebut mulai dieksekusi. Parameter kembalian dari fungsi dikirim dengan nilai ke pemanggil fungsi saat fungsi berakhir.

Memanggil fungsi yang nil menyebabkan panik runtime.

Kasus khusus, jika nilai kembalian dari sebuah fungsi atau method g sama jumlahnya dan dapat ditempatkan pada parameter-parameter di fungsi atau method f, maka pemanggilan f(g(parameter_dari_g)) akan memanggil f setelah nilai kembalian dari g diberikan sebagai parameter ke f secara berurutan. Pemanggilan dari f tidak boleh berisi parameter selain pemanggilan ke g, dan g paling tidak harus mengembalikan satu nilai. Jika f memiliki parameter akhir …​, ia akan diisi nilai kembalian dari g yang tersisa setelah penempatan parameter biasa sebelumnya.

func Split(s string, pos int) (string, string) {
	return s[0:pos], s[pos:]
}

func Join(s, t string) string {
	return s + t
}

if Join(Split(value, len(value)/2)) != value {
	log.Panic("test fails")
}

Pemanggilan sebuah method x.m() valid jika kumpulan method dari (tipe) x memiliki m dan daftar argumen dapat diberikan ke daftar parameter dari m. Jika x adalah alamat dan kumpulan method &x memiliki m, x.m() adalah singkatan untuk (&x).m():

var p Point
p.Scale(3.5)

Tidak ada perbedaan tipe method dan tidak ada literal method.

Mengirim argumen ke parameter …​

Jika f adalah variadic dengan parameter terakhir bertipe …​T, maka tipe dari p dalam f sama dengan tipe []T. Jika f dipanggil tanpa argumen untuk p, maka nilai yang dikirim ke p adalah nil. Sebaliknya, nilai yang dikirim adalah slice baru bertipe []T dengan array dasar yang elemennya adalah argumen-argumen aslinya, yang semuanya harus dapat disimpan ke T. Panjang dan kapasitas slice yaitu jumlah argumen dan bisa berbeda untuk setiap pemanggilan.

Diberikan fungsi dan pemanggilan

func Greeting(prefix string, who ...string)
Greeting("nobody")
Greeting("hello:", "Joe", "Anna", "Eileen")

dalam Greeting, who akan bernilai nil pada pemanggilan yang pertama, dan []string{"Joe", "Anna", "Eileen"} pada pemanggilan kedua.

Jika argumen terakhir dapat disimpan ke slice bertipe []T, maka akan dikirim tanpa diubah sebagai nilai untuk parameter …​T jika argumen diikuti oleh …​. Dalam kasus ini tidak ada slice baru yang dibuat.

Diberikan sebuah slice s dan pemanggilan

s := []string{"James", "Jasmine"}
Greeting("goodbye:", s...)

dalam Greeting, who akan bernilai sama dengan s dengan array dasar yang sama.

Operator-operator

Operator menggabungkan operan menjadi ekspresi.

Expression = UnaryExpr | Expression binary_op Expression .
UnaryExpr  = PrimaryExpr | unary_op UnaryExpr .

binary_op  = "||" | "&&" | rel_op | add_op | mul_op .
rel_op     = "==" | "!=" | "<" | "<=" | ">" | ">=" .
add_op     = "+" | "-" | "|" | "^" .
mul_op     = "*" | "/" | "%" | "<<" | ">>" | "&" | "&^" .

unary_op   = "+" | "-" | "!" | "^" | "*" | "&" | "<-" .

Operator pembandingan didiskusikan dibagian lain. Untuk operator binari lainnya, tipe operan haruslah identik kecuali pada operasi shift atau bila menggunakan konstanta tanpa tipe. Untuk operasi yang mengikutkan konstanta saja, lihat bagian pada ekspresi konstanta.

Kecuali untuk operasi shift, jika salah satu operan adalah konstanta dan operan lainnya bukan, konstanta tersebut secara implisit dikonversi ke tipe operan lainnya.

Operan sebelah kanan dalam ekspresi shift haruslah tipe integer atau konstanta tak bertipe yang dapat direpresentasikan oleh sebuah nilai bertipe uint. Jika operan sebelah kiri dari ekspresi shift adalah konstanta tanpa tipe, maka ia secara implisit dikonversi ke tipe yang diasumsikan jika ekspresi shift diganti oleh operan kiri.

var s uint = 33
var i = 1<<s                  // 1 bertipe int
var j int32 = 1<<s            // 1 bertipe int32; j == 0
var k = uint64(1<<s)          // 1 bertipe uint64; k == 1<<33
var m int = 1.0<<s            // 1.0 bertipe int; m == 0 jika int berukuran
                              // 32bit
var n = 1.0<<s == j           // 1.0 bertipe int32; n == true
var o = 1<<s == 2<<s          // 1 dan 2 bertipe int; o == true jika int
                              // berukuran 32bit
var p = 1<<s == 1<<33         // ilegal jika int berukuran 32bit: 1 bertipe
                              // int, namun 1<<33 int overflow
var u = 1.0<<s                // ilegal: 1.0 bertipe float64, tidak bisa shift
var u1 = 1.0<<s != 0          // ilegal: 1.0 bertipe float64, tidak bisa shift
var u2 = 1<<s != 1.0          // ilegal: 1 bertipe float64, tidak bisa shift
var v float32 = 1<<s          // ilegal: 1 bertipe float32, tidak bisa shift
var w int64 = 1.0<<33         // 1.0<<33 adalah ekspresi konstanta shift
var x = a[1.0<<s]             // 1.0 bertipe int; x == a,0>> jika int berukuran
                              // 32bit
var a = make([]byte, 1.0<<s)  // 1.0 bertipe int; len(a) == 0 jika int
                              // berukuran 32bit

Urutan operator

Operator unary memiliki urutan tertinggi. Secara operator ` dan `--` membentuk perintah, bukan ekspresi, maka mereka tidak berada dalam hierarki operator. Akibatnya, perintah `*p sama dengan (*p).

Ada lima tingkat urutan untuk operator binari. Operator perkalian yang paling tinggi, diikuti oleh operator penambahan, operator pembandingan, && (operasi logika AND), dan terakhir || (operator logika OR):

Precedence    Operator
    5             *  /  %  <<  >>  &  &^
    4             +  -  |  ^
    3             ==  !=  <  <=  >  >=
    2             &&
    1             ||

Operator binari pada tingkat yang sama akan di proses berurutan dari kiri ke kanan. Misalnya, x / y * z sama dengan (x / y) * z.

+x
23 + 3*x[i]
x <= f()
^a >> b
f() || g()
x == y+1 && <-chanPtr > 0

Operator aritmetika

Operator aritmetika dipakai pada nilai numerik dan menghasilkan nilai dengan tipe yang sama dengan operan pertama. Keempat operator standar aritmetika (, -, *, /) berlaku pada tipe integer, _floating-point_, dan complex; `` juga berlaku pada string. Operator shift dan logika bitwise berlaku pada integer saja.

+    penjumlahan            integer, float, nilai complex, string
-    pengurangan            integer, float, nilai complex
*    perkalian              integer, float, nilai complex
/    pembagian              integer, float, nilai complex
%    sisa pembagian         integer

&    bitwise AND            integer
|    bitwise OR             integer
^    bitwise XOR            integer
&^   bit clear (AND NOT)    integer

<<   shift kiri             integer << unsigned integer
>>   shift kanan            integer >> unsigned integer

Operator integer

Untuk dua integer bernilai x dan y, persamaan integer q = x / y dan sisa pembagian r = x % y memenuhi hubungan berikut:

x = q*y + r  dan  |r| < |y|

dengan x / y dibulatkan ke nol (pembagian dibulatkan).

 x     y     x / y     x % y
 5     3       1         2
-5     3      -1        -2
 5    -3      -1         2
-5    -3       1        -2

Satu-satunya pengecualian dari aturan ini yaitu jika sisa pembagian x adalah nilai negatif paling besar dari tipe x, persamaan q = x / -1 sama dengan x (dan r = 0) dikarenakan integer overflow dengan dua-komplemen:

			 x, q
int8                     -128
int16                  -32768
int32             -2147483648
int64    -9223372036854775808

Jika penyebut adalah sebuah konstanta, ia tidak boleh nol. Jika penyebut adalah nol pada saat runtime, panik runtime terjadi. Jika pembilang tidak negatif dan penyebut adalah sebuah konstanta kelipatan 2, maka pembagian bisa diganti dengan shift kanan, dan penghitungan sisa pembagian bisa diganti dengan operasi bitwise AND:

x     x / 4     x % 4     x >> 2     x & 3
 11      2         3         2          3
-11     -2        -3        -3          1

Operator shift menggeser operan sebelah kiri sebanyak jumlah yang disebutkan pada operan sebelah kanan, yang haruslah non-negatif. Jika bagian kanan operan shift negatif pada saat runtime, panik runtime terjadi. Operator shift menerapkan penggeseran aritmetika jika operan kiri adalah signed integer dan logika penggeseran jika ia adalah unsigned integer. Tidak ada batas atas pada nilai penggeser. Penggeseran berjalan seperti operan kiri digeser n kali dengan 1 untuk jumlah penggeser n. Hasilnya, x 1` sama dengan `x*2` dan `x 1 sama dengan x/2 tetapi dibulatkan ke nilai negatif tak terhingga.

Untuk operan integer, operator unary +, -, dan ^ didefinisikan sebagai berikut:

+x                        yaitu 0 + x
-x    negasi              yaitu 0 - x
^x    komplemen bitwise   yaitu m ^ x  dengan m = "semua bit diset ke 1" untuk unsigned x
                                       dan    m = -1 untuk signed x

Integer overflow

Untuk signed integer, operasi +, -, *, / dan operasi. Compiler bisa saja tidak mengoptimalkan sebuah kode dengan asumsi bahwa overflow tidak akan terjadi. Misalnya, compiler tidak mengasumsikan bahwa `x < x + 1 selalu bernilai true.

Operator floating-point

Untuk bilangan floating-point dan complex, +x sama dengan x, sementara -x adalah negasi dari x. Hasil pembagian dari floating-point atau complex dengan nol tidak dispesifikasikan oleh standar IEEE-754; apakah panik runtime terjadi adalah spesifik pada implementasi.

Implementasi bisa menggabungkan beberapa operasi floating-point menjadi sebuah operasi gabungan, kemungkinan antara beberapa perintah, dan menghasilkan nilai yang berbeda bila dieksekusi dan dibulatkan masing-masing. Konversi tipe floating-point secara eksplisit membulatkan ke presisi dari tipe target, menghindari penggabungan yang menyebabkan pembulatan.

Misalnya, beberapa arsitektur menyediakan instruksi "fused multiply add" (FMA) yang menghitung x*y + z tanpa pembulatan dari hasil x*y. Contoh berikut memperlihatkan kapan implementasi Go menggunakan instruksi tersebut:

// FMA dibolehkan menghitung r, karena x*y secara eksplisit tidak dibulatkan:
r  = x*y + z
r  = z;   r += x*y
t  = x*y; r = t + z
*p = x*y; r = *p + z
r  = x*y + float64(z)

// FMA tidak dibolehkan menghitung r, karena akan menghilangkan pembulatan
// dari x*y:
r  = float64(x*y) + z
r  = z; r += float64(x*y)
t  = float64(x*y); r = t + z

Penggabungan string

Beberapa string bisa digabungkan menggunakan operator ` atau operator penempatan `=:

s := "hi" + string(c)
s += " and good bye"

Penambahan string menghasilkan string yang baru dengan menggabungkan operan-operannya.

Operator pembandingan

Operator pembandingan membandingkan dua opera dan menghasilkan nilai boolean tak bertipe.

==    equal
!=    not equal
<     less
<=    less or equal
>     greater
>=    greater or equal

Dalam setiap pembandingan, operan pertama haruslah dapat disimpan ke tipe dari operan kedua, atau sebaliknya.

Operator persamaan == dan != berlaku untuk operan-operan yang dapat dibandingkan. Operator pengurutan <, , >, dan >= berlaku untuk operan-operan yang dapat diurutkan. Berikut definisi aturan-aturan dan hasil dari pembandingan:

  • Nilai boolean dapat dibandingkan. Dua boolean adalah sama jika keduanya adalah true atau false.

  • Nilai integer dapat dibandingkan dan diurutkan, dengan cara yang biasa.

  • Nilai floating-point dapat dibandingkan dan diurutkan, seperti yang didefinisikan oleh standar IEEE-754

  • Nilai complex dapat dibandingkan. Dua nilai complex u dan v adalah sama jika keduanya real(u) == real(v) dan imag(u) == imag(v).

  • Nilai string dapat dibandingkan dan diurutkan, secara leksikal per byte.

  • Nilai pointer dapat dibandingkan. Dua nilai pointer adalah sama jika ia menunjuk ke variabel yang sama atau jika keduanya bernilai nil. Pointer ke variabel dengan ukuran nol bisa jadi sama atau tidak.

  • Nilai channel dapat dibandingkan. Dua channel adalah sama jika keduanya dibuat oleh pemanggilan make yang sama atau jika keduanya bernilai nil.

  • Nilai interface dapat dibandingkan. Dua interface dikatakan sama jika keduanya memiliki tipe dinamis yang identik dan memiliki nilai dinamis yang sama atau jika keduanya bernilai nil.

  • Nilai x bertipe bukan interface X dan nilai t bertipe interface T dapat dibandingkan bila nilai dari tipe X dapat dibandingkan dan X mengimplementasikan T. Keduanya sama jika tipe dinamis t identik dengan X dan nilai dinamis t sama dengan x.

  • Nilai struct dapat dibandingkan jika semua field dapat dibandingkan. Dua nilai struct adalah sama jika setiap field yang bukan kosong adalah sama.

  • Nilai array dapat dibandingkan jika nilai dari tipe elemen array dapat dibandingkan. Dua nilai array adalah sama jika kedua elemen array adalah sama.

Pembandingan dua nilai interface dengan tipe dinamis yang identik menyebabkan panik runtime jika nilai dari tipe tersebut tidak bisa dibandingkan. Perilaku ini berlaku tidak saja pada pembandingan nilai interface langsung namun juga saat membandingkan array dari nilai interface atau struct dengan field bernilai interface.

Nilai slice, map, dan fungsi tidak bisa dibandingkan. Namun, sebagai kasus spesial, nilai slice, map, atau fungsi bisa jadi dibandingkan dengan pengidentifikasi nil. Pembandingan nilai pointer, channel, dan interface dengan nil juga dibolehkan dan mengikuti aturan di atas.

const c = 3 < 4            // c adalah konstanta boolean tak bertipe true

type MyBool bool
var x, y int
var (
	// Hasil dari pembandingan a yaitu boolean tak bertipe.
	// Aturan penempatan biasa berlaku.
	b3        = x == y // b3 bertipe bool
	b4 bool   = x == y // b4 bertipe bool
	b5 MyBool = x == y // b5 bertipe MyBool
)

Operator logika

Operator logika berlaku untuk nilai boolean dan menghasilkan tipe yang sama dengan operan. Operan kanan dievaluasi berdasarkan kondisi.

&&    kondisi AND    p && q  yaitu  "jika p maka q selainnya false"
||    kondisi OR     p || q  yaitu  "jika p maka true selainnya q"
!     NOT                !p  yaitu  "bukan p"

Operator alamat

Untuk sebuah operan x bertipe T, operasi alamat &x mengembalikan sebuah pointer bertipe *T ke x. Operan tersebut haruslah memiliki alamat, yaitu, sebuah variabel, pointer ke pointer, atau operasi pengindeksan slice; atau sebuah selector field dari operan struct yang memiliki alamat; atau sebuah operasi pengindeksan array dari array yang memiliki alamat;. Salah satu pengecualian dari kebutuhan alamat ini yaitu x mungkin bisa sebuah (dalam tanda kurung) literal komposit. Jika evaluasi dari x menyebabkan panik runtime, maka evaluasi dari &x juga akan panik.

Untuk sebuah operan x berupa pointer bertipe *T, pointer ke pointer dari *x menyatakan variabel bertipe T yang ditunjuk oleh x. Jika x adalah nil, evaluasi dari *x akan menyebabkan panik runtime.

&x
&a[f(2)]
&Point{2, 3}
*p
*pf(x)

var x *int = nil
*x   // menyebabkan panik runtime
&*x  // menyebabkan panik runtime

Operator terima

Untuk sebuah operan ch bertipe channel, nilai dari operasi terima ←ch adalah nilai yang diterima dari channel ch. Arah dari channel haruslah membolehkan operasi terima, dan tipe kembalian dari operasi terima yaitu tipe elemen dari channel. Ekspresi dari ←ch akan diblok sampai sebuah nilai tersedia. Operasi terima dari channel yang nil akan diblok selamanya. Operasi terima dari channel yang telah ditutup akan selalu diproses langsung, menghasilkan nilai kosong dari tipe elemen setelah semua nilai yang sebelumnya dikirim ke channel telah diterima semuanya.

v1 := <-ch
v2 = <-ch
f(<-ch)
<-strobe  // tunggu sampai waktu habis dan hiraukan nilai yang diterima

Ekspresi terima yang digunakan dalam penempatan atau inisiasi dari bentuk khusus

x, ok = <-ch
x, ok := <-ch
var x, ok = <-ch
var x, ok T = <-ch

menghasilkan nilai boolean yang melaporkan apakah komunikasi sukses atau tidak. Nilai dari ok adalah true jika nilai yang diterima dikirim oleh sebuah operasi pengiriman ke channel yang sukses, atau false jika nilainya adalah nilai kosong karena channel telah ditutup dan kosong.

Konversi

Sebuah konversi mengubah tipe dari sebuah ekspresi ke tipe yang ditentukan oleh konversi. Sebuah konversi bisa muncul secara literal dalam sumber kode, atau ia bisa diimplikasikan oleh konteks di mana ekspresi muncul.

Konversi eksplisit yaitu ekspresi dalam bentuk T(x) dengan T adalah sebuah tipe dan x adalah sebuah ekspresi yang dapat dikonversi ke tipe T.

Conversion = Type "(" Expression [ "," ] ")" .

Jika tipe diawali dengan operator * atau , atau jika tipe dimulai dengan kata kunci func dan tidak memiliki daftar kembalian, maka ia haruslah diberi tanda kurung bila diperlukan untuk menghindari kebingungan:

*Point(p)        // sama dengan *(Point(p))
(*Point)(p)      // p dikonversi ke *Point
<-chan int(c)    // sama dengan <-(chan int(c))
(<-chan int)(c)  // c dikonversi ke <-chan int
func()(x)        // penanda fungsi func() x
(func())(x)      // x dikonversi ke func()
(func() int)(x)  // x dikonversi ke func() int
func() int(x)    // x dikonversi ke func() int (tidak ambigu)

Sebuah konstanta bernilai x dapat dikonversi ke tipe T jika x dapat direpresentasikan oleh nilai dari T. Untuk kasus khusus, nilai konstanta x dapat secara eksplisit dikonversi ke sebuah tipe string menggunakan aturan yang sama dengan nilai non-konstanta x.

Mengonversi sebuah konstanta menghasilkan konstanta bertipe.

uint(iota)               // nilai iota bertipe uint
float32(2.718281828)     // 2.718281828 dengan tipe float32
complex128(1)            // 1.0 + 0.0i bertipe complex128
float32(0.49999999)      // 0.5 bertipe float32
float64(-1e-1000)        // 0.0 bertipe float64
string('x')              // "x" bertipe string
string(0x266c)           // "♬" bertipe string
MyString("foo" + "bar")  // "foobar" bertipe MyString
string([]byte{'a'})      // bukan konstanta: []byte{'a'} bukan konstanta
(*int)(nil)              // bukan konstanta: nil bukan konstanta, *int bukan
                         // sebuah tipe boolean, numerik, atau string
int(1.2)                 // ilegal: 1.2 tidak dapat direpresentasikan sebagai int
string(65.0)             // ilegal: 65.0 bukan konstanta integer

Sebuah nilai yang bukan konstanta x dapat dikonversi ke tipe T dalam kasus-kasus berikut:

  • x dapat disimpan ke T

  • mengindahkan tag dari struct (lihat bagian bawah), tipe x dan T memiliki tipe dasar yang identik..

  • mengindahkan tag dari struct (lihat bagian bawah), tipe x dan T adalah tipe pointer yang bukan tipe terdefinisi, dan tipe dasar pointer keduanya memiliki tipe dasar yang identik.

  • tipe x dan T adalah tipe integer atau floating point.

  • tipe x dan T adalah tipe complex.

  • x adalah integer atau slice byte atau rune dan T bertipe string

  • x adalah string dan T adalah sebuah slice byte atau rune.

tag dari struct diindahkan saat membanding tipe struct untuk identitas untuk tujuan konversi:

type Person struct {
	Name    string
	Address *struct {
		Street string
		City   string
	}
}

var data *struct {
	Name    string `json:"name"`
	Address *struct {
		Street string `json:"street"`
		City   string `json:"city"`
	} `json:"address"`
}

var person = (*Person)(data)  // mengindahkan tag, tipe dasarnya identik

Aturan-aturan spesifik berlaku untuk konversi (non-konstanta) antara tipe numerik atau dari dan ke tipe string. Konversi ini bisa mengubah representasi dari x dan membutuhkan biaya pada saat runtime. Konversi yang lainnya hanya mengubah tipe tetapi tidak representasi dari x.

Tidak ada mekanisme linguistik untuk mengonversi antara pointer dan integer. Paket unsafe mengimplementasikan fungsionalitas tersebut dengan batasan-batasan tertentu.

Konversi antara tipe numerik

Untuk konversi nilai numerik yang bukan konstanta, aturan-aturan berikut berlaku:

1. Saat mengonversi antara tipe integer, jika nilai adalah signed integer, maka sign -nya diperpanjang menjadi presisi tanpa batas; selain dari itu dinolkan. Nilainya kemudian dipotong untuk sesuai dengan ukuran tipe tujuan. Sebagai contohnya, jika v := uint16(0x10F0), maka uint32(int8(v)) == 0xFFFFFFF0. Konversi selalu menghasilkan nilai yang valid; tidak ada indikasi overflow. 2. Saat mengonversi bilangan floating point ke sebuah integer, maka pecahannya diindahkan (dibulatkan ke nol). 3. Saat mengonversi sebuah bilangan integer atau floating-point ke tipe floating-point, atau sebuah bilangan complex ke tipe complex lainnya, nilai akhirnya dibulatkan ke presisi yang ditentukan oleh tipe tujuan. Misalnya, nilai variabel x bertipe float32 disimpan menggunakan presisi tambahan diluar bilangan IEEE-754 32-bit, namun float32(x) merepresentasikan hasil dari pembulatan nilai x ke presisi 32-bit. Hal yang sama, x + 0.1 bisa memakai presisi lebih dari 32 bit, namun float32(x + 0.1) tidak.

Dalam semua konversi non-konstanta yang mengikutkan nilai floating-point atau complex, jika tipe tujuan tidak dapat merepresentasikan nilai maka konversi akan sukses namun nilai akhirnya bergantung pada implementasi.

Konversi ke dan dari tipe string

(1) Mengonversi nilai signed atau unsigned integer ke tipe string menghasilkan sebuah string yang berisi representasi UTF-8 dari integer. Nilai di luar rentang kode Unicode yang valid dikonversi ke "uFFFD".

string('a')       // "a"
string(-1)        // "\ufffd" == "\xef\xbf\xbd"
string(0xf8)      // "\u00f8" == "ø" == "\xc3\xb8"
type MyString string
MyString(0x65e5)  // "\u65e5" == "日" == "\xe6\x97\xa5"

(2) Mengonversi sebuah slice byte ke tipe string menghasilkan sebuah string yang setiap rangkaian byte adalah elemen dari slice.

string([]byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'})   // "hellø"
string([]byte{})                                     // ""
string([]byte(nil))                                  // ""

type MyBytes []byteConstant_expressions
string(MyBytes{'h', 'e', 'l', 'l', '\xc3', '\xb8'})  // "hellø"

(3) Mengonversi sebuah slice rune ke tipe string menghasilkan sebuah string yang merupakan penggabungan dari setiap nilai rune yang dikonversi ke string.

string([]rune{0x767d, 0x9d6c, 0x7fd4})   // "\u767d\u9d6c\u7fd4" == "白鵬翔"
string([]rune{})                         // ""
string([]rune(nil))                      // ""

type MyRunes []rune
string(MyRunes{0x767d, 0x9d6c, 0x7fd4})  // "\u767d\u9d6c\u7fd4" == "白鵬翔"

(4) Mengonversi sebuah nilai bertipe string ke slice byte menghasilkan sebuah slice yang rangkaian elemen-elemennya adalah byte dari string.

[]byte("hellø")   // []byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'}
[]byte("")        // []byte{}

MyBytes("hellø")  // []byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'}

(5) Mengonversi nilai bertipe string ke slice rune menghasilkan sebuah slice yang berisi setiap individu kode point Unicode dari string.

[]rune(MyString("白鵬翔"))  // []rune{0x767d, 0x9d6c, 0x7fd4}
[]rune("")                  // []rune{}

MyRunes("白鵬翔")           // []rune{0x767d, 0x9d6c, 0x7fd4}

Ekspresi konstanta

Ekspresi konstanta bisa berisi hanya operan-operan konstanta dan dievaluasi saat di-compile.

Konstanta tanpa tipe dari boolean, numerik, dan string bisa digunakan sebagai operan di mana pun selama legal menggunakan operan bertipe boolean, numerik, atau string.

Pembandingan konstanta selalu menghasilkan konstanta boolean tak bertipe. Jika operan kiri dari ekspresi shift adalah konstanta tak bertipe, hasilnya adalah konstanta integer; sebaliknya jika operan kiri yaitu konstanta bertipe maka hasilnya konstanta dengan tipe yang sama dengan operan kiri, yang haruslah bertipe integer.

Operasi lain pada konstanta tanpa tipe menghasilkan konstanta tanpa tipe dari jenis yang sama: yaitu konstanta boolean, integer, floating-point, complex, atau string. Jika operan dari operasi binari tidak bertipe (selain shift) memiliki jenis yang berbeda, hasilnya yaitu jenis operan yang muncul terakhir dalam daftar: integer, rune, floating-point, complex. Misalnya, konstanta integer tanpa tipe dibagi dengan konstanta complex tanpa tipe menghasilkan konstanta complex tanpa tipe.

const a = 2 + 3.0          // a == 5.0   (konstanta floating-point tanpa tipe)
const b = 15 / 4           // b == 3     (konstanta integer tanpa tipe)
const c = 15 / 4.0         // c == 3.75  (konstanta floating-point tanpa tipe)
const Θ float64 = 3/2      // Θ == 1.0   (tipe float64, 3/2 adalah pembagian integer)
const Π float64 = 3/2.     // Π == 1.5   (tipe float64, 3/2 adalah pembagian integer)
const d = 1 << 3.0         // d == 8     (konstanta integer tanpa tipe)
const e = 1.0 << 3         // e == 8     (konstanta integer tanpa tipe)
const f = int32(1) << 33   // illegal    (konstanta 8589934592 overflow int32)
const g = float64(2) >> 1  // illegal    (float64(2) adalah konstanta bertipe floating-point)
const h = "foo" > "bar"    // h == true  (konstanta boolean tanpa tipe)
const j = true             // j == true  (konstanta boolean tanpa tipe)
const k = 'w' + 1          // k == 'x'   (konstanta rune tanpa tipe)
const l = "hi"             // l == "hi"  (konstanta string tanpa tipe)
const m = string(k)        // m == "x"   (tipe string)
const Σ = 1 - 0.707i       //            (konstanta complex tanpa tipe)
const Δ = Σ + 2.0e-4       //            (konstanta complex tanpa tipe)
const Φ = iota*1i - 1/1i   //            (konstanta complex tanpa tipe)

Menerapkan fungsi bawaan complex ke konstanta integer, rune, atau floating-point tanpa tipe menghasilkan konstanta complex tanpa tipe.

const ic = complex(0, c)   // ic == 3.75i  (konstanta complex tak bertipe)
const iΘ = complex(0, Θ)   // iΘ == 1i     (tipe complex128)

Ekspresi konstanta selalu dievaluasi seperti yang tertulis; Nilai sementara dan konstanta itu sendiri bisa membutuhkan presisi yang lebih besar dari tipe yang didukung dalam bahasa. Berikut ini deklarasi yang legal:

const Huge = 1 << 100         // Huge == 1267650600228229401496703205376  (konstanta integer tak bertipe)
const Four int8 = Huge >> 98  // Four == 4                                (tipe int8)

Penyebut dari pembagian sebuah konstanta atau operasi penyisaan haruslah nol:

3.14 / 0.0   // ilegal: pembagian dengan nol

Nilai dari konstanta bertipe haruslah selalu secara akurat direpresentasikan oleh nilai dari tipe konstanta. Ekspresi konstanta berikut adalah ilegal:

uint(-1)     // -1 tidak dapat direpresentasikan sebagai uint
int(3.14)    // 3.14 tidak dapat direpresentasikan sebagai int
int64(Huge)  // 1267650600228229401496703205376 tidak dapat direpresentasikan sebagai int64
Four * 300   // operan 300 tidak dapat direpresentasikan sebagai int8 (tipe Four)
Four * 100   // hasil 400 tidak dapat direpresentasikan sebagai int8 (tipe Four)

Mask yang digunakan oleh operator komplemen unary bitwise ^ cocok dengan aturan untuk non-konstanta: semua mask adalah 1 untuk konstanta integer dan -1 untuk signed dan konstanta tak bertipe.

^1         // konstanta integer tak bertipe, sama dengan -2
uint8(^1)  // ilegal: sama dengan uint8(-2), -2 tidak dapat direpresentasikan sebagai uint8
^uint8(1)  // konstanta uint8 bertipe, sama dengan 0xFF ^ uint8(1) = uint8(0xFE)
int8(^1)   // sama dengan int8(-2)
^int8(1)   // sama dengan -1 ^ int8(1) = -2

Batasan implementasi: compiler bisa melakukan pembulatan saat menghitung ekspresi konstanta floating-point atau complex yang tak bertipe; lihat batasan implementasi dalam bagian konstanta. Pembulatan ini bisa menyebabkan ekspresi konstanta floating-point menjadi tidak valid dalam konteks integer, walaupun menjadi integral saat dihitung menggunakan presisi tanpa batas, dan sebaliknya.

Urutan evaluasi

Pada tingkat paket, inisiasi kebergantungan menentukan urutan evaluasi dari ekspresi inisiasi individu dalam deklarasi variabel. Sebaliknya, saat mengevaluasi operan dari sebuah ekspresi, penempatan, atau perintah kembalian, semua pemanggilan fungsi, pemanggilan method, dan operasi komunikasi dievaluasi secara leksikal terurut dari kiri-ke-kanan.

Contohnya, pada penempatan

y[f()], ok = g(h(), i()+x[j()], <-c), k()

pemanggilan fungsi dan komunikasi terjadi dengan urutan f(), h(), i(), j(), ←c, dan k(). Namun, urutan dari even-even tersebut dibandingkan dengan evaluasi dan pengindeksan dari x dan evaluasi dari y tidak dispesifikasikan.

a := 1
f := func() int { a++; return a }
x := []int{a, f()}            // x bisa jadi [1, 2] atau [2, 2]: urutan
                              // evaluasi antara a dan f() tidak ditentukan.
m := map[int]int{a: 1, a: 2}  // m bisa jadi {2: 1} atau {2: 2}: urutan
                              // evaluasi antara kunci dan nilai tidak ditentukan.
n := map[int]int{a: f()}      // n bisa jadi {2: 3} atau {3: 3}: urutan
                              // evaluasi antara kunci dan nilai tidak ditentukan.

Pada tingkat paket, inisiasi dependensi menimpa aturan kiri-ke-kanan untuk ekspresi inisiasi individu, tetapi tidak untuk operan-operan dalam setiap ekspresi:

var a, b, c = f() + v(), g(), sqr(u()) + v()

func f() int        { return c }
func g() int        { return a }
func sqr(x int) int { return x*x }

// fungsi u dan v independen terhadap semua variabel dan fungsi lainnya.

Pemanggilan fungsi terjadi dengan urutan u(), sqr(), v(), f(), v(), dan g().

Operasi floating-point dalam sebuah ekspresi dievaluasi menurut asosiatif dari operator. Tanda kurung secara eksplisit mempengaruhi evaluasi dengan menimpa asosiatif baku. Dalam ekspresi x+(y+z) penambahan y+z dilakukan sebelum menambahkan dengan x.

Perintah

Perintah-perintah mengontrol eksekusi.

Statement =
	Declaration | LabeledStmt | SimpleStmt |
	GoStmt | ReturnStmt | BreakStmt | ContinueStmt | GotoStmt |
	FallthroughStmt | Block | IfStmt | SwitchStmt | SelectStmt | ForStmt |
	DeferStmt .

SimpleStmt = EmptyStmt | ExpressionStmt | SendStmt | IncDecStmt | Assignment | ShortVarDecl .

Perintah penghenti

Sebuah perintah penghenti mencegah eksekusi dari semua perintah yang muncul setelah perintah penghenti dalam blok yang sama. Perintah-perintah berikut adalah penghenti:

  1. Sebuah perintah "return" atau "goto"

  2. Sebuah pemanggilan ke fungsi bawaan panic

  3. Sebuah blok yang mana daftar perintahnya berakhir dengan perintah penghenti.

  4. Sebuah perintah "if" yang mana:

    1. cabang "else" ada, dan

    2. kedua cabang adalah perintah penghenti.

  5. Sebuah perintah "for" yang mana:

    1. tidak ada perintah "break" yang mengacu ke perintah "for", dan

    2. kondisi pengulangan tidak ada.

  6. Sebuah perintah "switch" yang mana:

    1. tidak ada perintah "break" yang mengacu ke perintah "switch",

    2. ada pilihan "default", dan

    3. daftar perintah di setiap pilihan, termasuk "default", berakhir dengan sebuah perintah penghenti, atau bisa saja memiliki label perintah "fallthrough".

  7. Sebuah perintah "select" yang mana:

    1. tidak ada perintah "break" yang mengacu ke perintah "select", dan

    2. daftar perintah di setiap pilihan, termasuk "default" jika ada, berakhir dengan perintah penghenti.

  8. Sebuah perintah berlabel yang melabelkan perintah penghentian.

Semua perintah selain itu bukanlah penghenti.

Sebuah daftar perintah berakhir dalam sebuah perintah penghenti jika daftar tersebut tidak kosong dan perintah terakhirnya adalah penghenti.

Perintah kosong

Perintah kosong tidak melakukan apa pun.

EmptyStmt = .

Perintah berlabel

Sebuah perintah berlabel bisa menjadi target dari sebuah perintah goto, break, atau continue.

LabeledStmt = Label ":" Statement .
Label       = identifier .
Error: log.Panic("error encountered")

Perintah ekspresi

Dengan pengecualian dari fungsi bawaan khusus, pemanggilan fungsi dan method dan operasi menerima dapat muncul dalam konteks perintah. Perintah-perintah tersebut bisa diberi tanda kurung.

ExpressionStmt = Expression .

Fungsi-fungsi bawaan berikut tidak dibolehkan dalam konteks perintah:

append cap complex imag len make new real
unsafe.Alignof unsafe.Offsetof unsafe.Sizeof
h(x+y)
f.Close()
<-ch
(<-ch)
len("foo")  // ilegal jika len adalah fungsi bawaan

Perintah kirim

Sebuah perintah kirim mengirim sebuah nilai ke sebuah channel. Ekspresi channel haruslah bertipe channel, arah channel haruslah membolehkan operasi kirim, dan tipe dari nilai yang akan dikirim haruslah bisa disimpan ke tipe elemen dari channel.

SendStmt = Channel "<-" Expression .
Channel  = Expression .

Kedua ekspresi channel dan nilai dievaluasi sebelum komunikasi dimulai. Komunikasi akan ditahan sampai pengirim dan dilanjutkan. Sebuah pengiriman pada channel tanpa buffer dapat dilanjutkan jika sebuah penerima telah sedia. Sebuah pengiriman ke channel dengan buffer dapat dilanjutkan selama ada ruang dalam buffer. Sebuah pengiriman ke channel yang telah ditutup dilanjutkan dengan mengakibatkan panik runtime. Sebuah pengiriman terhadap channel yang nil akan ditahan selamanya.

ch <- 3  // kirim nilai 3 ke channel ch

Perintah IncDec

Perintah "" dan "—" menambah dan mengurangi operannya dengan konstanta tanpa tipe 1. Seperti halnya penempatan, operan tersebut haruslah beralamat atau ekspresi indeks dari sebuah map.

IncDecStmt = Expression ( "++" | "--" ) .

perintah penempatan berikut secara semantik sama:

Perintah IncDec     Assignment
x++                 x += 1
x--                 x -= 1

Penempatan

Assignment = ExpressionList assign_op ExpressionList .

assign_op = [ add_op | mul_op ] "=" .

Setiap operan sisi kiri haruslah beralamat, sebuah ekspresi indeks dari sebuah map, atau (hanya untuk penempatan =) pengidentifikasi kosong. Operan-operan bisa diberi tanda kurung.

x = 1
*p = f()
a[i] = 23
(k) = <-ch  // sama dengan: k = <-ch

Sebuah operasi penempatan x op= y yang mana op yaitu operator aritmetika binari yaitu sama dengan x = x op (y) namun hanya mengevaluasi x sekali saja. Konstruksi op= yaitu sebuah token tunggal. Dalam operasi penempatan, kedua daftar ekspresi kiri dan kanan haruslah berisi ekspresi tunggal dengan nilai, dan ekspresi bagian kiri haruslah bukan pengidentifikasi kosong.

a[i] <<= 2
i &^= 1<<n

Penempatan tuple memberi setiap individu elemen dari sebuah operasi multi-nilai ke sebuah daftar variabel. Ada dua bentuk. Bentuk pertama, operan bagian kanan yaitu ekspresi multi-nilai seperti pemanggilan fungsi, operasi channel atau map, atau sebuah tipe asersi. Jumlah operan pada sisi kiri harus sesuai dengan jumlah nilai. Misalnya, jika f adalah sebuah fungsi yang mengembalikan dua nilai,

x, y = f()

menempatkan nilai pertama ke x dan yang kedua ke y. Dalam bentuk kedua, jumlah operan pada sisi kiri haruslah sama dengan jumlah ekspresi di sisi kanan, setiap-tiapnya haruslah bernilai tunggal, dan ekspresi ke-n di sisi kanan ditempatkan ke operan ke-n di sisi kiri:

one, two, three = '一', '二', '三'

Pengidentifikasi kosong menyediakan cara untuk mengindahkan nilai di sisi kanan dalam sebuah penempatan:

_ = x       // mengevaluasi x namun mengindahkan nilainya
x, _ = f()  // mengevaluasi f() namun mengindahkan nilai kembalian kedua

Penempatan diproses dengan dua fase. Pertama, operan dari ekspresi indeks dan pembalikan pointer (termasuk pembalikan pointer implisit dalam selector) pada sisi kiri dan ekspresi di sisi kanan dievaluasi dengan urutan yang biasa. Kedua, penempatan dilakukan dengan urutan kiri-ke-kanan.

a, b = b, a  // tukar nilai a dan b

x := []int{1, 2, 3}
i := 0
i, x[i] = 1, 2  // set i = 1, x[0] = 2

i = 0
x[i], i = 2, 1  // set x[0] = 2, i = 1

x[0], x[0] = 1, 2  // set x[0] = 1, kemudian x[0] = 2 (sehingga x[0] == 2)

x[1], x[3] = 4, 5  // set x[1] = 4, kemudian panic saat menset x[3] = 5.

type Point struct { x, y int }
var p *Point
x[2], p.x = 6, 7  // set x[2] = 6, kemudian panic menset p.x = 7

i = 2
x = []int{3, 5, 7}
for i, x[i] = range x {  // set i, x[2] = 0, x[0]
	break
}
// setelah pengulangan, i == 0 and x == []int{3, 5, 3}

Dalam penempatan, setiap nilai haruslah dapat ditempatkan ke tipe dari operan yang mana ia ditempatkan, dengan kasus-kasus khusus berikut:

  1. Setiap nilai bertipe bisa ditempatkan ke pengidentifikasi kosong.

  2. Jika konstanta tak bertipe ditempatkan ke sebuah variabel bertipe interface atau pengidentifikasi kosong, maka konstanta tersebut secara implisit dikonversi ke tipe defaultnya.

  3. Jika nilai boolean tak bertipe ditempatkan ke sebuah variabel bertipe interface atau pengidentifikasi kosong, maka pertama ia dikonversi secara implisit ke tipe bool.

Perintah If

Perintah "if" menentukan kondisi eksekusi dari dua cabang menurut nilai dari sebuah ekspresi boolean. Jika evaluasi ekspresi bernilai true, maka cabang "if" yang dieksekusi, sebaliknya, jika ada, cabang "else" yang dieksekusi.

IfStmt = "if" [ SimpleStmt ";" ] Expression Block [ "else" ( IfStmt | Block ) ] .
if x > max {
	x = max
}

Ekspresi tersebut bisa didahului oleh perintah sederhana, yang dieksekusi sebelum ekspresi dievaluasi.

if x := f(); x < y {
	return x
} else if x > z {
	return z
} else {
	return y
}

Perintah switch

Perintah "switch" menyediakan eksekusi multi-cabang. Sebuah ekspresi atau spesifikasi tipe dibandingkan dengan setiap "case" dalam "switch" untuk menentukan cabang mana yang akan dieksekusi.

SwitchStmt = ExprSwitchStmt | TypeSwitchStmt .

Ada dua bentuk: switch dengan ekspresi dan switch dengan tipe. Pada switch dengan ekspresi, bagian "case" berisi ekspresi yang dibandingkan dengan nilai dari ekspresi switch. Pada switch dengan tipe, bagian "case" berisi tipe-tipe yang dibandingkan dengan tipe dari ekspresi switch. Ekspresi switch dievaluasi sekali dalam sebuah perintah switch.

Switch ekspresi

Dalam sebuah switch ekspresi, ekspresi switch dievaluasi dan ekspresi "case", yang tidak harus konstan, dievaluasi dari kiri-ke-kanan dan atas-ke-bawah; "case" yang pertama kali bernilai sama dengan ekspresi "switch" memulai eksekusi dari perintah-perintah yang berasosiasi dengan "case"; "case" yang lain akan dilewati. Jika tidak ada "case" yang sama dan ada sebuah case "default", maka perintah pada "default" lah yang akan dieksekusi. Paling banyak hanya ada satu case "default" dan ia dapat ditulis dibagian manapun dalam perintah "switch". Ekspresi "switch" yang kosong sama dengan nilai boolean true.

ExprSwitchStmt = "switch" [ SimpleStmt ";" ] [ Expression ] "{" { ExprCaseClause } "}" .
ExprCaseClause = ExprSwitchCase ":" StatementList .
ExprSwitchCase = "case" ExpressionList | "default" .

Jika ekspresi "switch" bernilai konstanta tanpa tipe, maka pertama kali secara implisit dikonversi ke tipe default-nya; jika nilainya ada boolean tanpa tipe, maka ia secara implisit dikonversi ke tipe bool. Nilai nil tidak bisa digunakan sebagai ekpsresi switch.

Jika sebuah ekspresi "case" tak bertipe, maka ia dikonversi secara implisit ke tipe dari ekspresi "switch". Untuk setiap ekspresi "case" x dan nilai t dari ekspresi "switch", x == t haruslah pembandingan yang valid.

Dengan kata lain, ekspresi "switch" diperlakukan untuk mendeklarasikan dan menginisiasi variabel sementara t tanpa tipe eksplisit; maka nilai dari t dibandingkan dengan setiap ekspresi "case" diuji untuk kesamaan.

Dalam sebuah "case" atau klausa "default", perintah terakhir bisa berupa perintah fallthrough (bisa berlabel) untuk mengindikasikan bahwa kontrol harus mengalir dari akhir klausa ke perintah pertama dari klausa selanjutnya. Selain kontrol mengalir ke akhir perintah "switch". Perintah "fallthrough" bisa muncul sebagai perintah terakhir kecuali pada klausa terakhir dari sebuah ekspresi "switch".

Ekspresi "switch" bisa diawali dengan perintah sederhana, yang dieksekusi sebelum ekspresi dievaluasi.

switch tag {
default: s3()
case 0, 1, 2, 3: s1()
case 4, 5, 6, 7: s2()
}

switch x := f(); {  // tanpa ada ekspresi switch berarti "true"
case x < 0: return -x
default: return x
}

switch {
case x < y: f1()
case x < z: f2()
case x == 4: f3()
}

Batasan implementasi: compiler bisa saja tidak membolehkan beberapa ekspresi "case" yang bernilai konstanta yang sama. Misalnya, compiler yang sekarang tidak membolehkan konstanta integer, floating-point, atau string yang duplikat dalam ekspresi "case".

Switch bertipe

Switch bertipe membandingkan tipe bukannya nilai. Perilakunya mirip dengan switch berekspresi. Ia ditandai dengan sebuah ekspresi switch yang memiliki bentuk dari sebuah asersi tipe menggunakan kata kunci type bukan tipe sebenarnya:

switch x.(type) {
// cases
}

Case kemudian dibandingkan dengan tipe sebenarnya T dibandingkan dengan tipe dinamis dari ekspresi x. Seperti pada asersi tipe, x haruslah bertipe interface, dan setiap tipe T dalam setiap "case" harus mengimplementasikan tipe dari x. Tipe-tipe yang terdaftar dalam "case"-"case" dari switch bertipe semuanya harus berbeda.

TypeSwitchStmt  = "switch" [ SimpleStmt ";" ] TypeSwitchGuard "{" { TypeCaseClause } "}" .
TypeSwitchGuard = [ identifier ":=" ] PrimaryExpr "." "(" "type" ")" .
TypeCaseClause  = TypeSwitchCase ":" StatementList .
TypeSwitchCase  = "case" TypeList | "default" .
TypeList        = Type { "," Type } .

TypeSwitchGuard bisa mengikutkan sebuah deklarasi variabel singkat. Bila bentuk tersebut digunakan, variabel dideklarasikan di akhir dari TypeSwitchCase dalam blok implisit dari setiap klausa. Dalam klausa-klausa dengan sebuah "case" yang berisi daftar hanya satu tipe, variabel memiliki tipe tersebut, sebaliknya, variabel memiliki tipe dari ekspresi dalam TypeSwitchGuard.

Selain tipe, sebuah "case" bisa menggunakan pengidentifikasi nil; "case" tersebut dipilih saat ekspresi dalam TypeSwitchGuard adalah nilai interface bernilai nil. Setidaknya hanya ada satu "case" dengan nil.

Diberikan sebuah ekspresi x bertipe interface{}, switch bertipe berikut:

switch i := x.(type) {
case nil:
	printString("x yaitu nil")             // tipe i yaitu tipe dari x (interface{})
case int:
	printInt(i)                            // tipe i yaitu int
case float64:
	printFloat64(i)                        // tipe i yaitu float64
case func(int) float64:
	printFunction(i)                       // tipe i yaitu func(int) float64
case bool, string:
	printString("type yaitu bool atau string")  // tipe i yaitu tipe x (interface{})
default:
	printString("type tidak diketahui")     // tipe i yaitu tipe x (interface{})
}

dapat ditulis ulang:

v := x  // x dievaluasi sekali
if v == nil {
	i := v                                 // tipe i yaitu x (interface{})
	printString("x yaitu nil")
} else if i, isInt := v.(int); isInt {
	printInt(i)                            // tipe i yaitu int
} else if i, isFloat64 := v.(float64); isFloat64 {
	printFloat64(i)                        // tipe i yaitu float64
} else if i, isFunc := v.(func(int) float64); isFunc {
	printFunction(i)                       // tipe i yaitu func(int) float64
} else {
	_, isBool := v.(bool)
	_, isString := v.(string)
	if isBool || isString {
		i := v                         // tipe i yaitu tipe x (interface{})
		printString("tipe yaitu bool atau string")
	} else {
		i := v                         // tipe i yaitu tipe x (interface{})
		printString("tipe tidak diketahui")
	}
}

Penjaga switch bertipe bisa diawali dengan perintah sederhana, yang dieksekusi sebelum penjaga dievaluasi.

Perintah "fallthrough" tidak dibolehkan dalam switch bertipe.

Perintah for

Sebuah perintah "for" menentukan eksekusi berulang dari sebuah blok. Ada tiga bentuk: iterasi bisa dikontrol oleh kondisi tunggal, sebuah klausa "for", atau sebuah klausa "range".

ForStmt = "for" [ Condition | ForClause | RangeClause ] Block .
Condition = Expression .

Perintah for dengan kondisi tunggal

Dalam bentuk sederhana, sebuah perintah "for" menentukan eksekusi berulang dari sebuah blok selama kondisi boolean bernilai true. Kondisi tersebut dievaluasi sebelum tiap iterasi. Jika kondisi tidak ada, maka ia sama dengan nilai boolean true.

for a < b {
	a *= 2
}

Perintah For dengan klausa for

Sebuah perintah "for" dengan ForClause juga dikontrol oleh kondisinya, namun sebagai tambahan bisa menspesifikasikan sebuah perintah init dan sebuah post, seperti sebuah penempatan, perintah peningkatan dan pengurangan. Perintah init bisa berupa sebuah deklarasi variabel singkat, namun perintah post tidak. Variabel yang dideklarasikan oleh perintah init digunakan pada setiap iterasi.

ForClause = [ InitStmt ] ";" [ Condition ] ";" [ PostStmt ] .
InitStmt = SimpleStmt .
PostStmt = SimpleStmt .
for i := 0; i < 10; i++ {
	f(i)
}

Jika tidak kosong, perintah init dieksekusi sekali sebelum mengevaluasi kondisi untuk iterasi pertama; perintah post dieksekusi setelah setiap eksekusi dari blok (dan hanya bila blok dieksekusi). Elemen dari ForClause bisa kosong namun titik-koma dibutuhkan kecuali bila hanya ada sebuah kondisi. Jika kondisi tidak ada, maka ia sama dengan nilai boolean true.

for cond { S() }    is the same as    for ; cond ; { S() }
for      { S() }    is the same as    for true     { S() }

Perintah For dengan klausa range

Perintah "for" dengan klausa "range" mengiterasi semua isi dari array, slice, string atau map, atau nilai yang diterima pada channel. Untuk setiap isi ia akan menempatkan nilai iterasi ke variabel iterasi jika ada dan kemudian mengeksekusi blok.

RangeClause = [ ExpressionList "=" | IdentifierList ":=" ] "range" Expression .

Ekspresi sebelah kanan dalam klausa "range" disebut ekpsresi range, yang bisa saja sebuah array, pointer ke array, slice, string, map, atau channel yang membolehkan operasi menerima. Seperti halnya pada penempatan, jika ada maka operan sebelah kiri harusalah beralamat atau berupa ekspresi indeks map; ia menyatakan variabel iterasi. Jika ekspresi range adalah sebuah channel, maka hanya satu variabel iterasi yang dibolehkan, selain itu bisa jadi dua variabel. Jika variabel iterasi terakhir adalah pengidentifikasi kosong, klausa range sama dengan klausa tanpa pengidentifikasi tersebut.

Ekspresi range x dievaluasi sekali sebelum memulai pengulangan, dengan satu pengecualian: jika paling tidak satu variabel iterasi ada dan len(x) adalah konstanta, ekspresi range tidak dievaluasi.

Pemanggilan fungsi pada bagian kiri dievaluasi sekali per iterasi. Untuk setiap iterasi, nilai iterasi dihasilkan sebagai berikut jika variabel iterasi ada:

Range expression                          1st value          2nd value

array or slice  a  [n]E, *[n]E, or []E    index    i  int    a[i]       E
string          s  string type            index    i  int    see below  rune
map             m  map[K]V                key      k  K      m[k]       V
channel         c  chan E, <-chan E       element  e  E
  1. Untuk sebuah array, pointer ke array, atau slice bernilai a, nilai iterasi indeks dihasilkan secara meningkat, dimulai dari elemen indeks 0. Jika satu variabel iterasi ada, pengulangan range menghasilkan nilai iterasi dari 0 sampai len(a) - 1 dan tidak mengindeks pada array atau slice itu sendiri. Untuk slice yang nil, jumlah iterasi adalah 0.

  2. Untuk nilai string, klausa "range" mengiterasi poin Unicode dalam string dimulai dari byte pada indeks ke 0. Pada iterasi yang sukses, nilai indeks yaitu indeks dari byte pertama dari kode poin UTF-8 dalam string, dan nilai kedua, bertipe rune, akan bernilai kode poin yang berkorespondensi. Jika iterasi menemui urutan UTF-8 yang tidak valid, nilai kedua yaitu 0xFFFD, yaitu karakter pengganti Unicode, dan iterasi selanjutnya akan meloncati sebuah byte dalam string.

  3. Urutan iterasi terhadap map tidak ditentukan dan tidak dijamin selalu sama dari satu iterasi dengan iterasi selanjutnya. Jika isi map yang belum diiterasi dihapus selama iterasi, nilai iterasi yang berkorespondensi tidak akan dihasilkan. Jika sebuah isi map dibuat selama iterasi, entri tersebut bisa saja muncul selama iterasi atau bisa saja dilewati. Pilihannya bisa beragam untuk setiap entri yang dibuat dan dari satu iterasi ke iterasi selanjutnya. Jika map adalah nil, jumlah iterasi adalah 0.

  4. Untuk channel, nilai iterasi yang dihasilkan adalah nilai yang dikirim ke channel sampai channel ditutup. Jika channel adalah nil, ekspresi range akan diblok selamanya.

Nilai iterasi ditempatkan ke variabel iterasi seperti pada perintah penempatan.

Variabel iterasi bisa dideklarasikan oleh klausa "range" menggunakan sebuah bentuk deklarasi variabel singkat (:=). Dalam kasus ini tipenya diset ke tipe dari nilai iterasi dan skop nya adalah blok dari perintah "for"; mereka dipergunakan dalam setiap iterasi. Jika variabel iterasi dideklarasikan di luar perintah "for", setelah eksekusi nilai mereka adalah nilai dari iterasi terakhir.

var testdata *struct {
	a *[7]int
}
for i, _ := range testdata.a {
	// testdata.a tidak pernah dievaluasi; len(testdata.a) adalah
	// konstanta
	// i memiliki rentang 0 to 6
	f(i)
}

var a [10]string
for i, s := range a {
	// tipe dari i yaitu int
	// tipe dari s yaitu string
	// s == a[i]
	g(i, s)
}

var key string
var val interface {}  // tipe elemen dari m ditempatkan ke val
m := map[string]int{"mon":0, "tue":1, "wed":2, "thu":3, "fri":4, "sat":5, "sun":6}
for key, val = range m {
	h(key, val)
}
// key == kunci map terakhir yang ditemukan dalam iterasi
// val == map[key]

var ch chan Work = producer()
for w := range ch {
	doWork(w)
}

// kosongkan channel
for range ch {}

Perintah go

Sebuah perintah "go" memulai eksekusi dari pemanggilan fungsi sebagai thread konkuren yang berdiri sendiri, atau disebut _goroutine, dalam ruang alamat yang sama.

GoStmt = "go" Expression .

Expression haruslah sebuah pemanggilan fungsi atau method; tidak bisa dalam tanda kurung kurawal. Pemanggilan fungsi bawaan dibatasi untuk perintah ekspresi.

Nilai fungsi dan parameter dievaluasi seperti biasa dalam goroutine yang dipanggil, tetapi tidak seperti pemanggilan biasa, eksekusi program tidak menunggu fungsi yang dipanggil untuk selesai. Namun, fungsi tersebut mulai dieksekusi secara independen dalam sebuah goroutine yang baru. Saat fungsi selesai, goroutine-nya juga selesai. Jika fungsi memiliki nilai kembalian, nilainya akan diindahkan saat fungsi selesai.

go Server()
go func(ch chan<- bool) { for { sleep(10); ch <- true }} (c)

Perintah select

Sebuah perintah "select" memilih sekumpulan operasi pengiriman atau penerimaan yang akan diproses. Ia mirip dengan perintah "switch" namun dengan semua "case" mengacu pada operasi komunikasi.

SelectStmt = "select" "{" { CommClause } "}" .
CommClause = CommCase ":" StatementList .
CommCase   = "case" ( SendStmt | RecvStmt ) | "default" .
RecvStmt   = [ ExpressionList "=" | IdentifierList ":=" ] RecvExpr .
RecvExpr   = Expression .

Sebuah "case" dengan RecvStmt bisa menempatkan hasil dari RecvExpr ke satu atau dua variabel, yang bisa dideklarasikan menggunakan deklarasi variabel singkat. RecvExpr haruslah sebuah operasi menerima (bisa pakai tanda kurung). Hanya boleh ada satu case "default" dan ia bisa muncul di mana pun dalam daftar case.

Eksekusi dari perintah "select" diproses dalam beberapa langkah:

  1. Untuk semua perintah case, operan channel dari operasi menerima dan ekspresi sebelah-kanan dan channel dari perintah pengiriman dievaluasi sekali, berurutan, saat memasuki perintah "select". Hasilnya yaitu sekumpulan channel yang akan menerima atau mengirim, dan nilai korespondensi yang akan dikirim. Efek samping dari evaluasi tersebut akan terjadi tanpa memperhatikan operasi komunikasi yang mana terpilih untuk diproses. Ekspresi di sebelah-kiri dari RecvStmt dengan deklarasi variabel singkat atau penempatan belum dievaluasi.

  2. Jika satu atau lebih komunikasi dapat diproses, salah satu dipilih secara acak. Sebaliknya, jika ada case "default", maka "default" lah yang dipilih. Jika tidak ada "default", perintah "select" menahan sampai paling tidak satu komunikasi dapat diproses.

  3. Kecuali bisa case yang terpilih adalah "default", operasi komunikasi yang diproses dieksekusi.

  4. Jika case yang terpilih adalah sebuah RecvStmt dengan deklarasi variabel singkat atau sebuah penempatan, ekspresi sebelah kiri dievaluasi dan nilai penerima ditempatkan.

  5. Daftar perintah dari case terpilih dieksekusi.

Secara komunikasi pada channel nil tidak pernah diproses, sebuah "select" dengan hanya channel-channel yang nil tanpa case "default" akan ditahan selamanya.

var a []int
var c, c1, c2, c3, c4 chan int
var i1, i2 int
select {
case i1 = <-c1:
	print("menerima ", i1, " dari c1\n")
case c2 <- i2:
	print("mengirim ", i2, " ke c2\n")
case i3, ok := (<-c3):  // sama dengan: i3, ok := <-c3
	if ok {
		print("menerima ", i3, " from c3\n")
	} else {
		print("c3 ditutup\n")
	}
case a[f()] = <-c4:
	// sama dengan:
	// case t := <-c4
	//	a[f()] = t
default:
	print("tidak ada komunikasi\n")
}

for {  // kirim seurutan bit yang random ke c
	select {
	case c <- 0:  // catatan: tidak ada perintah, tidak ada fallthrough.
	case c <- 1:
	}
}

select {}  // ditahan selamanya

Perintah return

Sebuah perintah "return" dalam sebuah fungsi F menghentikan eksekusi dari F, dan mengembalikan satu atau lebih nilai. Setiap fungsi yang di-defer oleh F dieksekusi sebelum F kembali ke pemanggilnya.

ReturnStmt = "return" [ ExpressionList ] .

Dalam sebuah fungsi tanpa tipe kembalian, perintah "return" tidak boleh menspesifikasikan nilai kembalian.

func noResult() {
	return
}

Ada tiga cara untuk mengembalikan nilai dari sebuah fungsi dengan tipe kembalian:

  1. Nilai kembalian bisa secara eksplisit didaftarkan dalam perintah "return". Setiap ekspresi haruslah bernilai tunggal dan dapat ditempatkan ke elemen yang berkorespondensi dengan tipe kembalian dari fungsi.

    func simpleF() int {
    	return 2
    }
    
    func complexF1() (re float64, im float64) {
    	return -7.0, -4.0
    }
  2. Daftar ekspresi dalam perintah "return" bisa berupa sebuah pemanggilan ke fungsi dengan banyak kembalian. Efeknya adalah setiap nilai kembalian dari fungsi tersebut ditempatkan ke variabel sementara dengan tipe yang sama, diikuti dengan perintah "return" mendaftar variabel-variabel tersebut, yang mana aturan dari kasus sebelumnya berlaku.

    func complexF2() (re float64, im float64) {
    	return complexF1()
    }
  3. Daftar ekspresi bisa kosong jika tipe kembalian fungsi memiliki nama untuk parameter kembalian. Parameter kembalian berlaku sebagai variabel lokal dan fungsi bisa menempatkan nilai kedalamnya bila diperlukan. Perintah "return" mengembalikan nilai dari variabel tersebut.

    func complexF3() (re float64, im float64) {
    	re = 7.0
    	im = 4.0
    	return
    }
    
    func (devnull) Write(p []byte) (n int, _ error) {
    	n = len(p)
    	return
    }

Bagaimanapun cara mereka dideklarasikan, semua nilai kembalian diinisiasi dengan nilai kosong untuk tipe mereka saat memasuki fungsi. Sebuah perintah "return" yang menentukan kembalian menset parameter-parameter kembalian sebelum fungsi-fungsi yang di-defer dieksekusi.

Batasan implementasi: compiler bisa tidak membolehkan daftar eksepresi yang kosong dalam sebuah perintah "return" jika entitas yang berbeda (konstanta, tipe, atau variabel) dengan nama yang sama dengan parameter kembalian berada dalam skop ditempat yang di "return".

func f(n int) (res int, err error) {
	if _, err := f(n-1); err != nil {
		return  // invalid return statement: err is shadowed
	}
	return
}

Perintah break

Sebuah perintah "break" menghentikan eksekusi dari perintah "for", "switch", "select" dalam fungsi yang sama.

BreakStmt = "break" [ Label ] .

Jika diberi label, ia haruslah yang mengurung perintah "for", "switch", "select", dan itulah yang eksekusinya akan dihentikan.

OuterLoop:
	for i = 0; i < n; i++ {
		for j = 0; j < m; j++ {
			switch a[i][j] {
			case nil:
				state = Error
				break OuterLoop
			case item:
				state = Found
				break OuterLoop
			}
		}
	}

Perintah continue

Sebuah perintah "continue" memulai iterasi selanjutnya dari pengulangan "for" di posisi perintah tersebut. Pengulangan "for" haruslah dalam fungsi yang sama.

ContinueStmt = "continue" [ Label ] .

Jika ada label, ia haruslah yang mengurung perintah "for", yang disanalah eksekusi akan dilanjutkan.

RowLoop:
	for y, row := range rows {
		for x, data := range row {
			if data == endOfRow {
				continue RowLoop
			}
			row[x] = data + bias(x, y)
		}
	}

Perintah goto

Sebuah perintah "goto" memindahkan kontrol ke perintah dengan label yang berkorespondensi dalam fungsi yang sama.

GotoStmt = "goto" Label .
goto error

Mengeksekusi perintah "goto" tidak boleh menyebabkan variabel menjadi skop yang belum ada dalam skop saat goto terjadi. Misalnya, contoh berikut

	goto L  // BAD
	v := 3
L:

adalah eror karena saat meloncat ke label L melewati terbuatnya v.

Sebuah perintah "goto" di luar blok tidak bisa meloncat ke label di dalam blok tersebut. Misalnya, contoh berikut:

if n%2 == 1 {
	goto L1
}
for n > 0 {
	f()
	n--
L1:
	f()
	n--
}

adalah eror karena label L1 berada dalam blok perintah "for" namun goto tidak dalam blok yang sama.

Perintah fallthrough

Sebuah perintah "fallthrough" memindahkan kontrol ke perintah pertama dari klausa "case" selanjutnya dalam sebuah ekspresi perintah "switch". Ia hanya bisa digunakan sebagai perintah terakhir dalam klausa tersebut.

FallthroughStmt = "fallthrough" .

Perintah defer

Sebuah perintah "defer" memanggil sebuah fungsi yang eksekusinya ditunda sampai fungsi yang mengurungnya selesai, baik karena fungsi tersebut mengeksekusi perintah "return", mencapai akhir dari badan fungsi, atau karena goroutine tersebut panik.

DeferStmt = "defer" Expression .

Si Expression haruslah sebuah pemanggilan fungsi atau method; tidak bisa di dalam tanda kurung. Pemanggilan fungsi bawaan terbatas untuk perintah ekspresi.

Setiap kali sebuah perintah "defer" dieksekusi, nilai dan parameter fungsi dievaluasi secara normal dan disimpan namun fungsi yang sebenarnya tidak dipanggil. Namun, fungsi yang ditunda dipanggil langsung sebelum fungsi kembali, dengan urutan terbalik dari saat mereka di-defer. Artinya, jika fungsi selesai lewat perintah "return", fungsi yang didefer dieksekusi setelah parameter kembalian diset oleh perintah "return" tersebut namun sebelum fungsi kembali ke pemanggilnya. Jika sebuah fungsi yang di-defer dievaluasi jadi nil, maka eksekusi akan panik saat fungsi dipanggil, bukan saat perintah "defer" dieksekusi.

Misalnya, jika fungsi yang di defer adalah sebuah literal fungsi dan fungsi yang mengurungnya memiliki parameter kembalian bernama yang berada dalam skop dari literal fungsi, maka fungsi yang di-defer bisa mengakses dan mengubah parameter kembalian sebelum mereka dikembalikan. Jika fungsi yang ditunda memiliki nilai kembalian, mereka diindahkan saat fungsi selesai. (Lihat juga bagian tentang penanganan panic.

lock(l)
defer unlock(l)  // unlock terjadi sebelum fungsi selesai

// cetak 3 2 1 0 sebelum fungsi selesai
for i := 0; i <= 3; i++ {
	defer fmt.Print(i)
}

// f mengembalikan 42
func f() (result int) {
	defer func() {
		// result diakses setelah ia di set ke 6 oleh perintah return.
		result *= 7
	}()
	return 6
}

Fungsi-fungsi bawaan

Fungsi-fungsi bawaan adalah pradeklarasi. Mereka dipanggil seperti halnya fungsi lainnya namun beberapa menerima tipe bukan ekspresi sebagai argumen pertamanya.

Fungsi-fungsi bawaan tidak memiliki tipe Go standar, jadi mereka hanya dapat muncul dalam ekspresi pemanggilan; mereka tidak bisa digunakan sebagai nilai fungsi.

Close

Untuk sebuah channel c, fungsi bawaan close(c) mencatat bahwa tidak ada lagi nilai yang dapat dikirim ke channel. Fungsi close akan eror jika c adalah channel yang bersifat menerima saja. Mengirim ke atau menutup channel yang telah ditutup menyebabkan panik run-time. Menutup channel yang nil juga menyebabkan panik run-time. Setelah memanggil close, dan setelah nilai yang dikirim sebelumnya telah diterima, operasi menerima akan mengembalikan nilai kosong dari tipe channel tersebut tanpa ditangguhkan. operasi menerima dengan beragam nilai mengembalikan nilai yang diterima berikut dengan indikasi tentang apakah channel telah ditutup.

Kapasitas dan panjang

Fungsi bawaan len dan cap menerima argumen berupa tipe dan mengembalikan nilai bertipe int. Implementasi menjamin bahwa kembalian selalu masuk dengan int.

Pemanggilan      Tipe argumen    Kembalian

len(s)           tipe string     panjang string dalam sekumpulan byte
                 [n]T, *[n]T     panjang array (== n)
                 []T             panjang slice
                 map[K]T         panjang map (jumlah kunci terdefinisi)
                 chan T          jumlah elemen antrean dalam buffer channel

cap(s)           [n]T, *[n]T     panjang array (== n)
                 []T             kapasitas slice
                 chan T          kapasitas buffer channel buffer

Kapasitas dari slice yaitu jumlah elemen yang telah dialokasi dalam array dibelakangnya. Relasi berikut selalu berlaku kapanpun:

0 <= len(s) <= cap(s)

Panjang dari slice, map atau channel yang nil selalu 0. Kapasitas dari slice atau channel yang nil selalu 0.

Ekspresi len(s) adalah konstanta jika s adalah konstanta string. Ekspresi len(s) dan cap(s) adalah konstanta jika tipe dari s adalah array atau pointer ke array dan ekspresi s tidak mengandung penerimaan channel atau pemanggilan fungsi (yang bukan konstanta); dalam kasus tersebut s tidak dievaluasi. Sebaliknya, pemanggilan len dan cap bukanlah konstanta dan s tidak dievaluasi.

const (
	c1 = imag(2i)                    // imag(2i) = 2.0 adalah konstanta
	c2 = len([10]float64{2})         // [10]float64{2} tidak mengandung
	                                 // pemanggilan fungsi
	c3 = len([10]float64{c1})        // [10]float64{c1} tidak mengandung
	                                 // pemanggilan fungsi
	c4 = len([10]float64{imag(2i)})  // imag(2i) adalah konstanta dan
                                         // tidak ada pemanggilan fungsi terjadi
	c5 = len([10]float64{imag(z)})   // invalid: imag(z) adalah
                                         // pemanggilan fungsi (bukan
					 // konstanta)
)
var z complex128

Alokasi

Fungsi bawaan new menerima sebuah tipe T, mengalokasikan penyimpanan untuk sebuah variabel dengan tipe tersebut pada saat run-time, dan mengembalikan sebuah nilai bertipe *T yang menunjuk kepadanya. Variabel tersebut diinisiasi seperti yang dijelaskan dalam bagian nilai awal.

new(T)

Misalnya

type S struct { a int; b float64 }
new(S)

mengalokasikan penyimpanan untuk sebuah variabel bertipe S, menginisiasinya (a=0, b=0.0), dan mengembalikan sebuah nilai bertipe *S yang berisi alamat dari alokasi.

Membuat slice, map, dan channel

Fungsi bawaan make menerima sebuah tipe T, yang haruslah bertipe slice, map, atau channel; diikuti oleh daftar ekspresi berdasarkan tipe. Ia mengembalikan sebuah nilai bertipe T (bukan *T). Memory diinisiasi seperti yang dijelaskan dalam bagian nilai awal.

Pemanggilan      Tipe T     Kembalian

make(T, n)       slice      slice bertipe T dengan panjang n dan kapasitas n
make(T, n, m)    slice      slice bertipe T dengan panjang n dan kapasitas m

make(T)          map        map bertipe T
make(T, n)       map        map bertipe T dengan alokasi mendekati n elemen

make(T)          channel    channel tanpa buffer bertipe T
make(T, n)       channel    channel dengan buffer bertipe T, buffer berukuran n

Setiap argumen ukuran n dan m haruslah bertipe integer atau konstanta tak bertipe. Argumen yang berupa konstanta haruslah tidak negatif dan dapat direpresentasikan oleh nilai bertipe int; jika ia berupa konstanta tak bertipe ia akan bertipe int. Jika n dan m diberikan dan keduanya adalah konstanta, maka n haruslah besar dari m. Jika n negatif atau besar dari m pada saat run-time, maka panik run-time akan terjadi.

s := make([]int, 10, 100)       // slice dengan len(s) == 10, cap(s) == 100
s := make([]int, 1e3)           // slice dengan len(s) == cap(s) == 1000
s := make([]int, 1<<63)         // ilegal: len(s) tidak dapat
                                // direpresentasikan oleh nilai bertipe int
s := make([]int, 10, 0)         // ilegal: len(s) > cap(s)
c := make(chan int, 10)         // channel dengan buffer berukuran 10
m := make(map[string]int, 100)  // map dengan ruang awal sekitar 100 elemen

Memanggil make dengan tipe map dan ukuran n akan membuat sebuah map dengan ruang awal yang dapat menyimpan n map elemen. Perilaku aslinya bergantung pada implementasi.

Menambah dan menyalin slice

Fungsi bawaan append dan copy membantu dalam operasi slice. Untuk kedua fungsi, hasilnya selalu independen walaupun memory yang diacu oleh argumen bertindihan.

Fungsi variadik append menambahkan kosong atau lebih nilai x ke s yang bertipe S, yang haruslah bertipe slice, dan mengembalikan hasil slice, yang juga bertipe S. Nilai x dikirim sebagai parameter bertipe …​T yang mana T adalah tipe elemen dari S dan aturan pengiriman parameter berlaku. Sebagai kasus khusus, append juga menerima argumen pertama yang dapat ditempatkan ke tipe []byte dengan argumen kedua bertipe string diikuti dengan …​. Bentuk ini menambahkan sejumlah byte ke string.

append(s S, x ...T) S  // T adalah elemen bertipe S

Jika kapasitas s tidak cukup besar untuk menampung nilai-nilai tambahan, append mengalokasikan sebuah array dasar yang baru yang lebih besar yang dapat menampung slice elemen yang alam dan nilai yang baru. Sebaliknya, append menggunakan array dasar yang sama.

s0 := []int{0, 0}
s1 := append(s0, 2)                // menambahkan sebuah element
                                   // s1 == []int{0, 0, 2}
s2 := append(s1, 3, 5, 7)          // menambahkan beberapa element
                                   // s2 == []int{0, 0, 2, 3, 5, 7}
s3 := append(s2, s0...)            // menambahkan slice
                                   // s3 == []int{0, 0, 2, 3, 5, 7, 0, 0}
s4 := append(s3[3:6], s3[2:]...)   // menambahkan slice yang tumpang tindih
                                   // s4 == []int{3, 5, 7, 2, 3, 5, 7, 0, 0}

var t []interface{}
t = append(t, 42, 3.1415, "foo")   // t == []interface{}{42, 3.1415, "foo"}

var b []byte
b = append(b, "bar"...)            // menambahkan isi string b == []byte{'b', 'a', 'r' }

Fungsi copy menyalin elemen slice dari sumber src ke tujuan dst dan mengembalikan jumlah elemen yang disalin. Kedua argumen haruslah memiliki elemen yang identik bertipe T dan harus dapat ditempatkan ke sebuah slice bertipe []T. Jumlah elemen yang disalin yaitu minium dari len(src) dan len(dst). Sebagai kasus khusus, copy juga menerima argumen tujuan yang dapat ditempatkan ke tipe []byte dengan argumen sumber bertipe string. Bentuk ini menyalin byte dari string ke slice byte.

copy(dst, src []T) int
copy(dst []byte, src string) int

Contoh:

var a = [...]int{0, 1, 2, 3, 4, 5, 6, 7}
var s = make([]int, 6)
var b = make([]byte, 5)
n1 := copy(s, a[0:])            // n1 == 6, s == []int{0, 1, 2, 3, 4, 5}
n2 := copy(s, s[2:])            // n2 == 4, s == []int{2, 3, 4, 5, 4, 5}
n3 := copy(b, "Hello, World!")  // n3 == 5, b == []byte("Hello")

Penghapusan elemen pada map

Fungsi bawaan delete menghapus elemen dengan kunci k dari sebuah map m. Tipe dari k haruslah dapat dapat ditempatkan ke tipe kunci dari m.

delete(m, k)  // hapus elemen m[k] dari map m

Jika map m adalah nil atau elemen m[k] tidak ada, delete adalah operasi kosong.

Manipulasi bilangan complex

Tiga fungsi menyusun dan mengurai bilangan complex. Fungsi bawaan complex membentuk sebuah nilai complex dari bagian real dan imajiner floating-point, sementara real dan imag mengekstrak bagian real dan imajiner dari sebuah nilai complex.

complex(realPart, imaginaryPart floatT) complexT
real(complexT) floatT
imag(complexT) floatT

Tipe dari argumen dan nilai kembalian saling berhubungan. Untuk complex, kedua argumen haruslah tipe floating-point yang sama dan tipe kembalian yaitu salah satu tipe complex berikut: complex64 untuk argumen bertipe float32, dan complex128 untuk argumen bertipe float64. Jika salah satu argumen adalah konstanta tak bertipe, maka ia secara implisit dikonversi ke tipe dari argumen lainnya. Jika kedua argumen dievaluasi ke konstanta tak bertipe, maka keduanya haruslah bilangan non-complex atau bagian imajinernya haruslah nol, dan nilai kembalian dari fungsi yaitu konstanta complex tak bertipe.

Untuk real dan imag, argumen haruslah bertipe complex, dan tipe kembalian berkorespondensi dengan tipe floating-point: float32 untuk argumen complex64, dan float64 untuk argumen complex128. Jika argumen dievaluasi menjadi konstanta tak bertipe, maka ia haruslah berupa angka, dan nilai kembalian dari fungsi yaitu konstanta floating-point tak bertipe.

Fungsi real dan imag keduanya membentuk kebalikan dari complex, sehingga untuk nilai z dari tipe complex Z, z == Z(complex(real(z), imag(z))).

Jika operan dari fungsi-fungsi tersebut semuanya adalah konstanta, maka nilai kembaliannya adalah konstanta.

var a = complex(2, -2)             // complex128
const b = complex(1.0, -1.4)       // konstanta complex tak bertipe 1 - 1.4i
x := float32(math.Cos(math.Pi/2))  // float32
var c64 = complex(5, -x)           // complex64
var s int = complex(1, 0)          // konstanta complex tak bertipe 1 + 0i
                                   // dapat dikonversi ke int
_ = complex(1, 2<<s)               // ilegal: 2 diasumsikan bertipe
                                   // floating-point, tidak bisa di- shift
var rl = real(c64)                 // float32
var im = imag(a)                   // float64
const c = imag(b)                  // konstanta tak bertipe -1.4
_ = imag(3 << s)                   // ilegal: 3 diasumsikan bertipe complex,
                                   // tidak bisa di-shift

Menangani panik

Dua fungsi bawaan, panic dan recover, membantu dalam melaporkan dan menangani panik run-time dan kondisi eror yang terdefinisi pada program.

func panic(interface{})
func recover() interface{}

Saat mengeksekusi sebuah fungsi F, pemanggilan panic yang eksplisit atau panik run-time menghentikan eksekusi dari F. Fungsi apa pun yang ditunda oleh F kemudian dieksekusi. Selanjutnya, fungsi apa pun yang ditunda oleh pemanggil F dijalankan, dan begitu seterusnya sampai fungsi paling atas dalam goroutine. Di titik tersebut, program dihentikan dan kondisi eror dilaporkan, termasuk nilai argumen ke panic. Urutan penghentian ini disebut dengan panicking.

panic(42)
panic("unreachable")
panic(Error("cannot parse"))

Fungsi recover membolehkan sebuah program mengatur perilaku dari goroutine yang panik. Misalkan sebuah fungsi G menunda fungsi D yang memanggil recover dan panik terjadi dalam sebuah fungsi pada goroutine yang sama yang mana G dieksekusi. Saat menjalankan fungsi-fungsi yang ditunda mencapai D, nilai kembalian dari pemanggilan recover di dalam D akan berisi argumen yang dikirim pada saat panic dipanggil. Jika D kembali secara normal, tanpa memulai panic yang baru, maka urutan panicking berhenti. Dalam kasus ini, status dari fungsi yang dipanggil antara G dan pemanggilan panic diindahkan, dan eksekusi normal dilanjutkan. Fungsi apa pun yang ditunda oleh G sebelum D maka kemudian dijalankan dan eksekusi dari G dihentikan dengan mengembalikan ke pemanggilnya.

Nilai kembalian dari recover adalah nil jika salah satu kondisi berikut berlaku:

  • argumen dari panic adalah nil;

  • goroutine tersebut tidak panik;

  • recover tidak secara langsung dipanggil oleh fungsi yang di-defer.

Fungsi protect dalam contoh di bawah memanggil fungsi g dan melindungi pemanggil dari panik run-time yang disebabkan oleh g.

func protect(g func()) {
	defer func() {
		log.Println("done")  // Println dieksekusi secara normal
		                     // bahkan jika ada panic
		if x := recover(); x != nil {
			log.Printf("run time panic: %v", x)
		}
	}()
	log.Println("start")
	g()
}

Bootstrapping

Implementasi yang sekarang menyediakan beberapa fungsi bawaan yang berguna selama bootstrapping. Fungsi-fungsi tersebut didokumentasikan demi kelengkapan namun tidak dijamin akan tetap ada dalam bahasa Go. Mereka tidak memiliki nilai kembalian.

Fungsi    Perilaku

print     mencetak semua argumen; format dari argumen bergantung pada implementasi
println   seperti print namun mencetak spasi antara argumen dan baris baru di akhir

Batasan implementasi: print dan println tidak perlu menerima tipe argumen yang beragam, namun harus mendukung pencetakan tipe boolean, numerik, dan string.

Paket

Program Go dibentuk dengan mengaitkan paket-paket. Sebuah paket dibentuk dari satu atau lebih berkas sumber yang berisi deklarasi konstanta, tipe, variabel, dan fungsi yang dimiliki oleh paket tersebut yang mana dapat diakses di semua berkas dari paket yang sama. Elemen-elemen tersebut bisa diekspor dan digunakan di paket lainnya.

Organisasi berkas sumber

Setiap berkas sumber terdiri dari sebuah klausa paket yang mendefinisikan paket di mana ia berada, diikuti oleh deklarasi import yang bisa saja kosong yang mendeklarasikan paket-paket yang isinya akan digunakan, diikuti oleh sekumpulan deklarasi fungsi, tipe, variabel, dan konstanta.

SourceFile       = PackageClause ";" { ImportDecl ";" } { TopLevelDecl ";" } .

Klausa paket

Sebuah klausa paket memulai berkas sumber dan mendefinisikan paket yang mana berkas tersebut berada.

PackageClause  = "package" PackageName .
PackageName    = identifier .

PackageName tidak boleh berupa pengidentifikasi kosong.

package math

Sekumpulan berkas yang berbagi PackageName yang sama membentuk implementasi dari sebuah paket. Implementasi Go bisa saja mengharuskan semua berkas sumber untuk sebuah paket berada dalam direktori yang sama.

Deklarasi import

Sebuah deklarasi import menyatakan bahwa berkas sumber yang berisi deklarasi tersebut bergantung pada fungsionalitas dari paket yang diimpor (Inisiasi dan eksekusi program) dan membolehkan akses ke pengidentifikasi yang diekspor dari paket tersebut. Deklarasi import bisa memberi nama pada pengidentifikasi (PackageName) untuk digunakan sebagai akses dan ImportPath yang menentukan paket yang akan diimpor.

ImportDecl       = "import" ( ImportSpec | "(" { ImportSpec ";" } ")" ) .
ImportSpec       = [ "." | PackageName ] ImportPath .
ImportPath       = string_lit .

PackageName digunakan dalam pengidentifikasi nama untuk mengakses pengidentifikasi yang diekspor dari paket dalam berkas sumber yang mengimpor. Ia dideklarasikan dalam blok berkas. Jika PackageName tidak ada, maka default-nya adalah pengidentifikasi yang dispesifikasikan dalam klausa paket dari paket yang diimpor. Jika tanda titik (.) digunakan bukan sebuah nama, semua pengidentifikasi yang diekspor yang dideklarasikan dalam blok paket tersebut akan dideklarasikan dalam blok berkas sumber yang mengimpor dan harus diakses tanpa nama.

Interpretasi dari ImportPath bergantung pada implementasi namun biasanya berupa sebuah substring dari nama berkas penuh dari paket yang di-compile dan bisa jadi relatif terhadap repositori dari paket yang dipasang.

Batasan implementasi: Compiler bisa membatasi ImportPath menjadi string yang tidak kosong menggunakan hanya karakter yang berada dalam kategori umum Unicode L, M, N, P, dan S (karakter grafik tanpa spasi) dan bisa juga tidak mengikutkan karaketer !"#$%&'()*,:;⇐>?[]^{|}` dan karakter pengganti Unicode U+FFFD.

Asumsi kita telah mengkompilasi sebuah paket yang berisi klausa paket package math, yang mengekspor fungsi Sin, dan memasang paket yang telah dikompilasi dalam berkas yang diidentifikasi dengan "lib/math". Tabel berikut mengilustrasikan bagaimana Sin diakses dalam berkas yang mengimpor paket tersebut setelah deklarasi impor yang beragam.

Deklarasi impor             Nama lokal dari Sin

import   "lib/math"         math.Sin
import m "lib/math"         m.Sin
import . "lib/math"         Sin

Deklarasi import menyatakan sebuah relasi dependensi antara paket yang mengimpor dan yang diimpor. Paket tidak boleh mengimpor dirinya sendiri, baik secara langsung maupun tidak langsung, atau secara langsung mengimpor sebuah paket tanpa menggunakan pengidentifikasi yang diekspornya. Untuk mengimpor sebuah paket hanya untuk efek sampingnya saja (inisiasi), gunakan pengidentifikasi kosong sebagai nama paket yang eksplisit:

import _ "lib/math"

Sebuah contoh paket

Berikut sebuah paket Go yang mengimplementasikan saringan bilangan prima secara konkuren.

package main

import "fmt"

// Kirim seurutan 2, 3, 4, … ke channel 'ch'.
func generate(ch chan<- int) {
	for i := 2; ; i++ {
		ch <- i  // Send 'i' to channel 'ch'.
	}
}

// Salin nilai dari channel 'src' ke channel 'dst',
// menghapus angka yang dapat dibagi dengan 'prime'.
func filter(src <-chan int, dst chan<- int, prime int) {
	for i := range src {  // Pengulangan terhadap nilai yang diterima dari 'src'.
		if i%prime != 0 {
			dst <- i  // Kirim 'i' ke channel 'dst'.
		}
	}
}

// Penyaring bilangan prima: Proses filter Daisy-chain berjalan bersamaan.
func sieve() {
	ch := make(chan int)  // Buat channel baur.
	go generate(ch)       // Jalankan generate() sebagai goroutine.
	for {
		prime := <-ch
		fmt.Print(prime, "\n")
		ch1 := make(chan int)
		go filter(ch, ch1, prime)
		ch = ch1
	}
}

func main() {
	sieve()
}

Inisiasi dan eksekusi program

Nilai kosong

Saat tempat dialokasikan untuk sebuah variabel, baik lewat deklarasi atau pemanggilan new, atau saat sebuah nilai baru dibuat, baik lewat literal komposit atau sebuah pemanggilan ke make, dan tidak ada inisiasi eksplisit yang diberikan, maka variabel atau nilai diberikan nilai default. Setiap elemen dari variabel tersebut di set ke nilai kosong bagi tipenya: false untuk boolean, 0 untuk tipe numerik, "" untuk string, dan nil untuk pointer, fungsi, interface, slice, channel, dan map. Inisiasi ini berlaku secara rekursif, jadi misalkan setiap elemen dari struct array akan memiliki field-filed yang di-nolkan jika tidak ada nilai yang dispesifikasikan.

Dua deklarasi berikut adalah sama:

var i int
var i int = 0

Setelah

type T struct { i int; f float64; next *T }
t := new(T)

hal berikut berlaku:

t.i == 0
t.f == 0.0
t.next == nil

Hal yang sama juga benar untuk

var t T

Inisiasi paket

Dalam sebuah paket, inisiasi variabel di tingkat paket diproses secara bertahap, dengan setiap tahap memilih variabel paling awal dalam urutan deklarasi yang tidak memiliki dependensi terhadap variabel-variabel yang belum diinisiasi.

Lebih tepatnya, variabel di tingkat paket dianggap siap untuk inisiasi jika ia belum diinisiasi dan juga tidak punya ekspresi inisiasi atau ekspresi inisiasinya tidak memiliki kebergantungan terhadap variabel yang belum diinisiasi. Inisiasi diproses dengan secara berulang menginisiasi variabel tingkat-paket selanjutnya yang dideklarasikan di awal dan siap untuk inisiasi, sampai tidak ada lagi variabel siap untuk diinisiasi.

Jika ada variabel yang tetap belum diinisiasi saat proses selesai, variabel tersebut adalah bagian dari satu atau lebih putaran inisiasi, dan program tidak valid.

Sejumlah variabel pada bagian kiri dari sebuah deklarasi variabel yang diinisiasi oleh ekspresi tunggal (multi-nilai) pada bagian kanan, diinisiasi secara bersamaan: Jika ada variabel di sisi kiri diinisiasi, maka semua variabel tersebut diinisiasi dalam tahap yang sama.

var x = a
var a, b = f() // a dan b diinisiasi bersamaan, sebelum x diinisiasi.

Variabel kosong diperlakukan seperti halnya variabel lainnya dalam deklarasi.

Urutan deklarasi dari variabel yang dideklarasikan dalam beberapa berkas ditentukan oleh urutan yang mana berkas tersebut diberikan ke compiler: Variabel yang dideklarasikan dalam berkas yang pertama dideklarasikan sebelum variabel lain dideklarasikan di berkas yang kedua, dan seterusnya.

Analisis dependensi tidak bergantung pada nilai sebenarnya dari variabel, hanya pada referensi leksikal dalam sumber berkas, dianalisis secara transitif. Misalnya, jika ekspresi inisiasi variabel x mengacu ke sebuah fungsi yang isinya mengacu pada variabel y, maka x bergantung pada y. Secara spesifik:

  • Sebuah referensi ke sebuah variabel atau fungsi adalah sebuah pengidentifikasi yang menyatakan variabel atau fungsi tersebut.

  • Sebuah referensi ke sebuah method m yaitu sebuah nilai method atau ekspresi method dalam bentuk t.m, yang mana tipe (statik) dari t bukanlah tipe interface, dan method m berada dalam kumpulan method dari t. Apakah nilai fungsi yang dihasilkan dari t.m dipanggil tidak dipedulikan.

  • Sebuah variabel, fungsi, atau method x bergantung pada sebuah variabel y jika ekspresi inisiasi x atau badan (untuk fungsi dan method) berisi referensi ke y atau ke fungsi atau method yang bergantung ke y.

Sebagai contoh, diberikan deklarasi berikut

var (
	a = c + b  // == 9
	b = f()    // == 4
	c = f()    // == 5
	d = 3      // == 5 after initialization has finished
)

func f() int {
	d++
	return d
}

urutan inisiasi adalah d, b, c, a. Ingatlah bahwa urutan sub-ekspresi dalam ekspresi inisiasi tidak penting: a = c + b dan a = b + c menghasilkan urutan inisiasi yang sama dalam contoh tersebut.

Analisis dependensi dilakukan per paket; hanya referensi yang mengacu ke variabel, fungsi, dan method (bukan interface) yang dideklarasikan dalam paket yang sekarang yang diperhatikan. Jika ada kebergantungan data tersembunyi ada antara variabel, urutan inisiasi antara variabel tersebut tidak ditentukan.

Misalnya, diberikan deklarasi berikut

var x = I(T{}).ab()   // x memiliki dependensi tersembunyi pada a dan b
var _ = sideEffect()  // tidak berkaitan dengan x, a, atau b
var a = b
var b = 42

type I interface      { ab() []int }
type T struct{}
func (T) ab() []int   { return []int{a, b} }

Variabel a akan diinisiasi setelah b namun apakah x diinisiasi sebelum b, antara b dan a, atau setelah a, atau pada saat sideEffect() dipanggil (sebelum atau setelah x diinisiasi) tidak ditentukan.

Variabel juga dapat diinisiasi menggunakan fungsi bernama init yang dideklarasikan dalam blok paket, tanpa argumen dan parameter kembalian.

func init() { ... }

Satu atau lebih fungsi tersebut bisa didefinisikan per paket, bahkan dalam berkas sumber yang sama. Dalam blok paket, pengidentifikasi init hanya dapat digunakan untuk mendeklarasikan fungsi init, namun pengidentifikasi tersebut tidak dideklarasikan. Oleh sebab itu fungsi init tidak dapat diacu di mana pun dalam sebuah program.

Sebuah paket tanpa import diinisiasi dengan menempatkan nilai awal ke semua variabel di tingkat paket diikuti oleh pemanggilan fungsi init dengan urutan sebagaimana mereka muncul dalam sumber, bisa jadi dalam banyak berkas, saat diberikan kepada compiler. Jika sebuah paket memiliki import, paket-paket yang diimpor diinisiasi sebelum menginisiasi paket itu sendiri. Jika banyak paket mengimpor sebuah paket, maka paket yang diimpor hanya diinisiasi sekali. Saat mengimpor paket, secara konstruksi, menjamin bahwa tidak ada dependensi siklis.

Inisiasi paket —inisiasi variabel dan pemanggilan fungsi init— terjadi dalam sebuah goroutine, secara sekuensial, satu paket satu per satu. Fungsi init bisa meluncurkan goroutine yang lain, yang dapat berjalan secara konkuren dengan kode inisiasi. Namun, inisiasi selalu berurutan untuk fungsi init: ia tidak akan memanggil init selanjutnya sebelum yang sebelumnya selesai.

Untuk memastikan perilaku inisiasi yang dapat direproduksi, sistem pembangunan dianjurkan memberikan beberapa berkas dalam paket yang sama dengan urutan nama berkas secara leksikal kepada compiler.

Eksekusi program

Sebuah program yang lengkap dibuat dengan mengaitkan sebuah paket yang tidak diimpor bernama paket main dengan semua paket yang diimpornya, secara transitif. Paket main haruslah memiliki nama paket main dan mendeklarasikan sebuah fungsi main yang tidak menerima argumen dan tidak memiliki kembalian.

func main() { … }

Eksekusi program dimulai dengan menginisiasi paket main dan kemudian memanggil fungsi main. Bila pemanggilan fungsi tersebut selesai, maka program keluar. Ia tidak menunggu goroutine yang lain untuk selesai.

Eror

Tipe error pradeklarasi didefinisikan sebagai

type error interface {
	Error() string
}

Interface error adalah konvensi untuk merepresentasikan kondisi eror, dengan nilai nil merepresentasikan tidak ada eror. Misalnya, sebuah fungsi yang membaca data dari sebuah berkas bisa didefinisikan:

func Read(f *File, b []byte) (n int, err error)

Panik run-time

Eror eksekusi seperti mencoba membaca indeks array di luar batas memicu panik run-time sama seperti dengan memanggil fungsi bawaan panic dengan nilai tipe interface runtime.Error. Tipe tersebut memenuhi tipe interface pradeklarasi error. Nilai error sebenarnya yang merepresentasikan kondisi eror run-time tidak ditentukan.

package runtime

type Error interface {
	error
	// dan mungkin method-method lainnya.
}

Konsiderasi sistem

Packet unsafe

Paket bawaan unsafe, dikenal oleh compiler dan dapat diakses lewat path import "unsafe", menyediakan fasilitas untuk pemrograman tingkat-rendah termasuk operasi yang melanggar sistem tipe. Sebuah paket yang menggunakan unsafe haruslah diperiksa secara manual demi keamanan tipe dan bisa jadi tidak portabel. Paket tersebut menyediakan interface berikut:

package unsafe

type ArbitraryType int  // singkatan untuk tipe Go beragam; bukan tipe sebenarnya.
type Pointer *ArbitraryType

func Alignof(variable ArbitraryType) uintptr
func Offsetof(selector ArbitraryType) uintptr
func Sizeof(variable ArbitraryType) uintptr

Sebuah Pointer adalah tipe pointer namun nilai Pointer tidak boleh di-acu ulang. Pointer atau nilai dari tipe dasar uintptr dapat dikonversi ke tipe dari tipe dasar Pointer dan sebaliknya. Efek dari mengonversi antara Pointer dan uintptr adalah terdefinisi secara implementasi.

var f float64
bits = *(*uint64)(unsafe.Pointer(&f))

type ptr unsafe.Pointer
bits = *(*uint64)(ptr(&f))

var p ptr = nil

Fungsi Alignof dan Sizeof menerima sebuah ekspresi x dengan tipe apa pun dan mengembalikan ukuran tipe dari sebuah variabel hipotesis v seolah-olah v dideklarasikan lewat var v = x.

Fungsi Offsetof menerima sebuah selector s.f, menandakan field f dari struct yang bernotasi s atau *s, dan mengembalikan posisi dari field dalam byte yang relatif terhadap alamat struct. Jika f adalah field yang ditanam, ia haruslah dapat diakses tanpa pointer lewat field-field dari struct. Untuk sebuah struct s dengan field f:

uintptr(unsafe.Pointer(&s)) + unsafe.Offsetof(s.f) == uintptr(unsafe.Pointer(&s.f))

Arsitektur komputer bisa jadi membutuhkan alamat memory supaya diratakan; supaya alamat sebuah variabel merupakan kelipatan dari sebuah faktor, perataan tipe dari variabel. Fungsi Alignof menerima ekspresi yang menyatakan sebuah variabel tipe apa pun dan mengembalikan perataan dari (tipe dari) variabel dalam sekumpulan byte. Untuk sebuah variabel x:

uintptr(unsafe.Pointer(&x)) % unsafe.Alignof(x) == 0

Pemanggilan dari Alignof, Offsetof, dan Sizeof adalah ekspresi konstanta bertipe uintptr.

Jaminan ukuran dan perataan

Untuk tipe-tipe numerik, ukuran-ukuran berikut dijamin:

tipe                                  ukuran dalam byte

byte, uint8, int8                     1
uint16, int16                         2
uint32, int32, float32                4
uint64, int64, float64, complex64     8
complex128                           16

Properti perataan minimum berikut selalu berlaku:

  1. Untuk sebuah variabel x tipe apa pun: unsafe.Alignof(x) paling tidak 1.

  2. Untuk sebuah variabel x bertipe struct: unsafe.Alignof(x) adalah nilai terbesar dari semua unsafe.Alignof(x.f) untuk setiap field f dari x, namun paling tidak 1.

  3. Untuk sebuah variabel x bertipe array: unsafe.Alignof(x) sama dengan perataan dari variabel dari tipe elemen dari array.

type T struct {}

type T2 struct {
	b byte
	i int64
}

var t T
fmt.Println(unsafe.Alignof(t))  // 1

var t2 T2
fmt.Println(unsafe.Alignof(t2)) // 8, ukuran terbesar yaitu dari field i.

var si []int32
fmt.Println(unsafe.Alignof(si)) // 4, ukuran elemen dari si yaitu int32.

Sebuah tipe struct atau array memiliki ukuran nol jika ia tidak mengandung field (atau elemen) yang memiliki ukuran lebih besar dari nol. Dua variabel berukuran nol yang berbeda bisa saja memiliki alamat yang sama di memory.