Table of Contents

03 – Pengetahuan Umum

Command

Berikut daftar CLI atau Command Line Interface pada golang :

Command go mod init

Command go mod init digunakan untuk inisialisasi project pada Go (menggunakan Go Modules). Untuk nama project bisa menggunakan apapun, tapi umumnya adalah disamakan dengan nama direktori.

Nama project ini penting karena nantinya berpengaruh pada import path sub packages yang ada dalam project tersebut.

				
					mkdir <nama-project>
cd <nama-project>
go mod init <nama-project>
				
			
Command go run

Command go run digunakan untuk eksekusi file program (file ber-ekstensi .go). Cara penggunaannya dengan menuliskan command tersebut diikuti argumen nama file.

Berikut adalah contoh penerapan go run untuk eksekusi file program main.go yang tersimpan di path project-pertama yang path tersebut sudah diinisialisasi menggunakan go mod init.

				
					cd project-pertama
go run main.go
				
			

Command go run hanya bisa digunakan pada file yang nama package-nya adalah main.

Jika ada banyak file yang package-nya main dan file-file tersebut berada pada satu direktori level dengan file utama, maka eksekusinya adalah dengan menuliskan semua file sebagai argument command go run. Contohnya bisa dilihat pada kode berikut.

				
					go run main.go library.go
				
			
Command go build

Command ini digunakan untuk mengkompilasi file program.

Sebenarnya ketika eksekusi program menggunakan go run, terjadi proses kompilasi juga. File hasil kompilasi akan disimpan pada folder temporary untuk selanjutnya langsung dieksekusi.

Berbeda dengan go build, command ini menghasilkan file executable atau binary pada folder yang sedang aktif. Contohnya bisa dilihat pada kode berikut.

				
					go build
				
			

Pada contoh di atas, project project-pertama di-build, menghasilkan file baru pada folder yang sama, yaitu project-pertama.exe, yang kemudian dieksekusi. Default-nya nama project akan otomatis dijadikan nama binary.

Untuk nama executable sendiri bisa diubah menggunakan flag -o. Contoh:

				
					go build -o <nama-executable>
go build -o program.exe
				
			

Untuk sistem operasi non-windows, tidak perlu menambahkan akhiran .exe pada nama binary.

Command go get

Command go get digunakan untuk men-download package. Sebagai contoh saya ingin men-download package Kafka driver untuk Go pada project project-pertama.

				
					cd project-pertama
go get github.com/segmentio/kafka-go
				
			

Command go get harus dijalankan dalam folder project. Jika dijalankan di-luar project maka akan diunduh ke pada GOPATH.

Command go mod tidy

Command go mod tidy digunakan untuk memvalidasi dependensi. Jika ada dependensi yang belum ter-download, maka akan otomatis di-download.

Command go mod vendor

Vendoring di Go merupakan kapabilitas untuk mengunduh semua dependency atau 3rd party, untuk disimpan di lokal dalam folder project, dalam folder bernama vendor.

Dengan adanya folder tersebut, maka Go tidak akan lookup 3rd party ke cache folder, melainkan langsung mempergunakan yang ada dalam folder vendor. Jadi tidak perlu download lagi dari internet.

				
					go mod vendor
				
			
Layout Format String

Di golang kita akan banyak menggunakan method fmt Print, Printf, dan Sprintf dengan layout format string seperti %s%d%.2f, dan lainnya; untuk keperluan menampilkan output ke layar ataupun untuk memformat string.

Layout format string digunakan dalam konversi data ke bentuk string. Contohnya seperti %.3f yang untuk konversi nilai double ke string dengan 3 digit desimal. untuk lebih jelas berikut daftar format penggunaannya.

The verbs:

General:

%v	the value in a default format
	when printing structs, the plus flag (%+v) adds field names
%#v	a Go-syntax representation of the value
%T	a Go-syntax representation of the type of the value
%%	a literal percent sign; consumes no value

Boolean:

%t	the word true or false

Integer:

%b	base 2
%c	the character represented by the corresponding Unicode code point
%d	base 10
%o	base 8
%O	base 8 with 0o prefix
%q	a single-quoted character literal safely escaped with Go syntax.
%x	base 16, with lower-case letters for a-f
%X	base 16, with upper-case letters for A-F
%U	Unicode format: U+1234; same as "U+%04X"

Floating-point and complex constituents:

%b	decimalless scientific notation with exponent a power of two,
	in the manner of strconv.FormatFloat with the 'b' format,
	e.g. -123456p-78
%e	scientific notation, e.g. -1.234456e+78
%E	scientific notation, e.g. -1.234456E+78
%f	decimal point but no exponent, e.g. 123.456
%F	synonym for %f
%g	%e for large exponents, %f otherwise. Precision is discussed below.
%G	%E for large exponents, %F otherwise
%x	hexadecimal notation (with decimal power of two exponent), e.g. -0x1.23abcp+20
%X	upper-case hexadecimal notation, e.g. -0X1.23ABCP+20

String and slice of bytes (treated equivalently with these verbs):

%s	the uninterpreted bytes of the string or slice
%q	a double-quoted string safely escaped with Go syntax
%x	base 16, lower-case, two characters per byte
%X	base 16, upper-case, two characters per byte

Slice:

%p	address of 0th element in base 16 notation, with leading 0x

Pointer:

%p	base 16 notation, with leading 0x
The %b, %d, %o, %x and %X verbs also work with pointers,
formatting the value exactly as if it were an integer.

The default format for %v is:

bool:                    %t
int, int8 etc.:          %d
uint, uint8 etc.:        %d, %#x if printed with %#v
float32, complex64, etc: %g
string:                  %s
chan:                    %p
pointer:                 %p

For compound objects, the elements are printed using these rules, recursively, laid out like this:

struct:             {field0 field1 ...}
array, slice:       [elem0 elem1 ...]
maps:               map[key1:value1 key2:value2 ...]
pointer to above:   &{}, &[], &map[]

Width is specified by an optional decimal number immediately preceding the verb. If absent, the width is whatever is necessary to represent the value. Precision is specified after the (optional) width by a period followed by a decimal number. If no period is present, a default precision is used. A period with no following number specifies a precision of zero. Examples:

%f     default width, default precision
%9f    width 9, default precision
%.2f   default width, precision 2
%9.2f  width 9, precision 2
%9.f   width 9, precision 0
Komentar

Komentar biasa dimanfaatkan untuk menyisipkan catatan pada kode program, menulis penjelasan/deskripsi mengenai suatu blok kode, atau bisa juga digunakan untuk me-remark kode (men-non-aktifkan kode yg tidak digunakan). Komentar akan diabaikan ketika kompilasi maupun eksekusi program.

Ada 2 jenis komentar di Go, inline & multiline. Pada pembahasan ini akan dijelaskan tentang penerapan dan perbedaan kedua jenis komentar tersebut.

Komentar Inline

Penulisan komentar jenis ini di awali dengan tanda double slash (//) lalu diikuti pesan komentarnya. Komentar inline hanya berlaku untuk satu baris pesan saja. Jika pesan komentar lebih dari satu baris, maka tanda // harus ditulis lagi di baris selanjutnya.

Komentar Multiline

Komentar yang cukup panjang akan lebih rapi jika ditulis menggunakan teknik komentar multiline. Ciri dari komentar jenis ini adalah penulisannya diawali dengan tanda /* dan diakhiri */.

Variabel

Go mengadopsi dua jenis penulisan variabel, yaitu yang dituliskan tipe data-nya, dan juga yang tidak. Kedua cara tersebut valid dan tujuannya sama, pembedanya hanya cara penulisannya saja.

Pada chapter ini akan dikupas tuntas tentang macam-macam cara deklarasi variabel.

Deklarasi Variabel Beserta Tipe Data

Go memiliki aturan cukup ketat dalam hal penulisan variabel. Ketika deklarasi, tipe data yg digunakan harus dituliskan juga. Istilah lain dari konsep ini adalah manifest typing.

				
					package main

import "fmt"

func main() {
    var firstName string = "john"

    var lastName string
    lastName = "wick"

    fmt.Printf("halo %s %s!\n", firstName, lastName)
}
				
			

Keyword var di atas digunakan untuk deklarasi variabel, contohnya bisa dilihat pada firstName dan lastName.

Nilai variabel firstName diisi langsung ketika deklarasi, berbeda dibanding lastName yang nilainya diisi setelah baris kode deklarasi, hal seperti ini diperbolehkan di Go.

Deklarasi Variabel Tanpa Tipe Data

Selain manifest typing, Go juga mengadopsi konsep type inference, yaitu metode deklarasi variabel yang tipe data-nya ditentukan oleh tipe data nilainya, cara kontradiktif jika dibandingkan dengan cara pertama. Dengan metode jenis ini, keyword var dan tipe data tidak perlu ditulis.

				
					// menggunakan var, tanpa tipe data, menggunakan perantara "="
var firstName = "john"

// tanpa var, tanpa tipe data, menggunakan perantara ":="
lastName := "wick"
				
			

Kedua deklarasi di atas maksudnya sama. Silakan pilih yang nyaman di hati.

Tanda := hanya digunakan sekali di awal pada saat deklarasi. Untuk assignment nilai selanjutnya harus menggunakan tanda =, contoh.

Deklarasi Multi Variabel

Pengisian nilai juga bisa dilakukan bersamaan pada saat deklarasi. Caranya dengan menuliskan nilai masing-masing variabel berurutan sesuai variabelnya dengan pembatas koma (,).

				
					var first, second, third string
first, second, third = "satu", "dua", "tiga"
				
			
Variabel Underscore _

Go memiliki aturan unik yang jarang dimiliki bahasa lain, yaitu tidak boleh ada satupun variabel yang menganggur. Artinya, semua variabel yang dideklarasikan harus digunakan. Jika ada variabel yang tidak digunakan tapi dideklarasikan, error akan muncul pada saat kompilasi dan program tidak akan bisa di-run.

Underscore (_) adalah reserved variable yang bisa dimanfaatkan untuk menampung nilai yang tidak dipakai. Bisa dibilang variabel ini merupakan keranjang sampah.

				
					_ = "belajar Golang"
_ = "Golang itu mudah"
name, _ := "john", "wick"
				
			

Pada contoh di atas, variabel name akan berisikan text john, sedang nilai wick ditampung oleh variabel underscore, menandakan bahwa nilai tersebut tidak akan digunakan.

Variabel underscore adalah predefined, jadi tidak perlu menggunakan := untuk pengisian nilai, cukup dengan = saja. Namun khusus untuk pengisian nilai multi variabel yang dilakukan dengan metode type inference, boleh di dalamnya terdapat variabel underscore.

Biasanya variabel underscore sering dimanfaatkan untuk menampung nilai balik fungsi yang tidak digunakan.

Perlu diketahui, bahwa isi variabel underscore tidak dapat ditampilkan. Data yang sudah masuk variabel tersebut akan hilang. Ibaratkan variabel underscore seperti blackhole, objek apapun yang masuk ke dalamnya, akan terjebak selamanya di-dalam singularity dan tidak akan bisa keluar

Deklarasi Variabel Menggunakan Keyword new

Keyword new digunakan untuk membuat variabel pointer dengan tipe data tertentu. Nilai data default-nya akan menyesuaikan tipe datanya.

				
					name := new(string)

fmt.Println(name)   // 0x20818a220
fmt.Println(*name)  // ""
				
			

Variabel name menampung data bertipe pointer string. Jika ditampilkan yang muncul bukanlah nilainya melainkan alamat memori nilai tersebut (dalam bentuk notasi heksadesimal). Untuk menampilkan nilai aslinya, variabel tersebut perlu di-dereference terlebih dahulu, menggunakan tanda asterisk (*).

Deklarasi Variabel Menggunakan Keyword make

Keyword ini hanya bisa digunakan untuk pembuatan beberapa jenis variabel saja, yaitu:

  • channel
  • slice
  • map

Dan lagi, mungkin banyak yang akan bingung. Ketika sudah masuk ke pembahasan masing-masing poin tersebut, akan terlihat apa kegunaan dari keyword make ini.

Tipe Data

Go mengenal beberapa jenis tipe data, di antaranya adalah tipe data numerik (desimal & non-desimal), string, dan boolean.

Tipe Data Numerik Non-Desimal

Tipe data numerik non-desimal atau non floating point di Go ada beberapa jenis. Secara umum ada 2 tipe data kategori ini yang perlu diketahui.

  • uint, tipe data untuk bilangan cacah (bilangan positif).
  • int, tipe data untuk bilangan bulat (bilangan negatif dan positif).

Kedua tipe data di atas kemudian dibagi lagi menjadi beberapa jenis, dengan pembagian berdasarkan lebar cakupan nilainya, detailnya bisa dilihat di tabel berikut.

Tipe dataCakupan bilangan
uint80 ↔ 255
uint160 ↔ 65535
uint320 ↔ 4294967295
uint640 ↔ 18446744073709551615
uintsama dengan uint32 atau uint64 (tergantung nilai)
bytesama dengan uint8
int8-128 ↔ 127
int16-32768 ↔ 32767
uint32-2147483648 ↔ 2147483647
int64-9223372036854775808 ↔ 9223372036854775807
intsama dengan int32 atau int64 (tergantung nilai)
runesama dengan int32

Dianjurkan untuk tidak sembarangan dalam menentukan tipe data variabel, sebisa mungkin tipe yang dipilih harus disesuaikan dengan nilainya, karena efeknya adalah ke alokasi memori variabel. Pemilihan tipe data yang tepat akan membuat pemakaian memori lebih optimal, tidak berlebihan.

				
					var positiveNumber uint8 = 89
var negativeNumber = -1243423644

fmt.Printf("bilangan positif: %d\n", positiveNumber)
fmt.Printf("bilangan negatif: %d\n", negativeNumber)
				
			

Variabel positiveNumber bertipe uint8 dengan nilai awal 89. Sedangkan variabel negativeNumber dideklarasikan dengan nilai awal –1243423644. Compiler secara cerdas akan menentukan tipe data variabel tersebut sebagai int32 (karena angka tersebut masuk ke cakupan tipe data int32).

Template %d pada fmt.Printf() digunakan untuk memformat data numerik non-desimal.

Tipe Data Numerik Desimal

Tipe data numerik desimal yang perlu diketahui ada 2, float32 dan float64. Perbedaan kedua tipe data tersebut berada di lebar cakupan nilai desimal yang bisa ditampung. Untuk lebih jelasnya bisa merujuk ke spesifikasi IEEE-754 32-bit floating-point numbers.

				
					var decimalNumber = 2.62

fmt.Printf("bilangan desimal: %f\n", decimalNumber)
fmt.Printf("bilangan desimal: %.3f\n", decimalNumber)
				
			

Pada kode di atas, variabel decimalNumber akan memiliki tipe data float32, karena nilainya berada di cakupan tipe data tersebut.

Template %f digunakan untuk memformat data numerik desimal menjadi string. Digit desimal yang akan dihasilkan adalah 6 digit. Pada contoh di atas, hasil format variabel decimalNumber adalah 2.620000. Jumlah digit yang muncul bisa dikontrol menggunakan %.nf, tinggal ganti n dengan angka yang diinginkan. Contoh: %.3f maka akan menghasilkan 3 digit desimal, %.10f maka akan menghasilkan 10 digit desimal.

Tipe Data bool (Boolean)

Tipe data bool berisikan hanya 2 variansi nilai, true dan false. Tipe data ini biasa dimanfaatkan dalam seleksi kondisi dan perulangan.

				
					var exist bool = true
fmt.Printf("exist? %t \n", exist)
				
			

Gunakan %t untuk memformat data bool menggunakan fungsi fmt.Printf().

Tipe Data string

Ciri khas dari tipe data string adalah nilainya di apit oleh tanda quote atau petik dua (). Contoh penerapannya:

				
					var message string = "Halo"
fmt.Printf("message: %s \n", message)
				
			

Selain menggunakan tanda quote, deklarasi string juga bisa dengan tanda grave accent/backticks (`), tanda ini terletak di sebelah kiri tombol 1. Keistimewaan string yang dideklarasikan menggunakan backtics adalah membuat semua karakter di dalamnya tidak di escape, termasuk \n, tanda petik dua dan tanda petik satu, baris baru, dan lainnya. Semua akan terdeteksi sebagai string.

				
					var message = `Nama saya "John Wick".
Salam kenal.
Mari belajar "Golang".`

fmt.Println(message)
				
			

Ketika dijalankan, output akan muncul sama persisi sesuai nilai variabel message di atas. Tanda petik dua akan muncul, baris baru juga muncul, sama persis.

Nilai nil & Zero Value

nil bukan merupakan tipe data, melainkan sebuah nilai. Variabel yang isi nilainya nil berarti memiliki nilai kosong.

Semua tipe data yang sudah dibahas di atas memiliki zero value (nilai default tipe data). Artinya meskipun variabel dideklarasikan dengan tanpa nilai awal, tetap akan ada nilai default-nya.

  • Zero value dari string adalah “” (string kosong).
  • Zero value dari boll adalah false.
  • Zero value dari tipe numerik non-desimal adalah 0.
  • Zero value dari tipe numerik desimal adalah 0.0.

Zero value berbeda dengan nil. Nil adalah nilai kosong, benar-benar kosong. nil tidak bisa digunakan pada tipe data yang sudah dibahas di atas. Ada beberapa tipe data yang bisa di-set nilainya dengan nil, di antaranya:

  • pointer
  • tipe data fungsi
  • slice
  • map
  • channel
  • interface kosong atau any (yang merupakan alias dari
  • interface{})

 

Konstanta

Konstanta adalah jenis variabel yang nilainya tidak bisa diubah. Inisialisasi nilai hanya dilakukan sekali di awal, setelah itu variabel tidak bisa diubah nilainya.

Penggunaan Konstanta

Data seperti pi (22/7), kecepatan cahaya (299.792.458 m/s), adalah contoh data yang tepat jika dideklarasikan sebagai konstanta daripada variabel, karena nilainya sudah pasti dan tidak berubah.

Cara penerapan konstanta sama seperti deklarasi variabel biasa, selebihnya tinggal ganti keyword var dengan const.

				
					const firstName string = "john"
fmt.Print("halo ", firstName, "!\n")
				
			

Teknik type inference bisa diterapkan pada konstanta, caranya yaitu cukup dengan menghilangkan tipe data pada saat deklarasi.

				
					const lastName = "wick"
fmt.Print("nice to meet you ", lastName, "!\n")
				
			
Operator

Chapter ini membahas mengenai macam operator yang bisa digunakan di Go. Secara umum terdapat 3 kategori operator: aritmatika, perbandingan, dan logika.

Operator Aritmatika

Operator aritmatika adalah operator yang digunakan untuk operasi yang sifatnya perhitungan. Go mendukung beberapa operator aritmatika standar, list-nya bisa dilihat di tabel berikut.

TandaPenjelasan
+penjumlahan
-pengurangan
*perkalian
/pembagian
%modulus / sisa hasil pembagian

Contoh penggunaan:

				
					var value = (((2 + 6) % 3) * 4 - 2) / 3
				
			
Operator Perbandingan

Operator perbandingan digunakan untuk menentukan kebenaran suatu kondisi. Hasilnya berupa nilai boolean, true atau false.

Tabel di bawah ini berisikan operator perbandingan yang bisa digunakan di Go.

TandaPenjelasan
==apakah nilai kiri sama dengan nilai kanan
!=apakah nilai kiri tidak sama dengan nilai kanan
<apakah nilai kiri lebih kecil daripada nilai kanan
<=apakah nilai kiri lebih kecil atau sama dengan nilai kanan
>apakah nilai kiri lebih besar dari nilai kanan
>=apakah nilai kiri lebih besar atau sama dengan nilai kanan

Contoh penggunaan:

				
					var value = (((2 + 6) % 3) * 4 - 2) / 3
var isEqual = (value == 2)

fmt.Printf("nilai %d (%t) \n", value, isEqual)
				
			

Pada kode di atas, terdapat statement operasi aritmatika yang hasilnya ditampung oleh variabel value. Selanjutnya, variabel tersebut dibandingkan dengan angka 2 untuk dicek apakah nilainya sama. Jika iya, maka hasilnya adalah true, jika tidak maka false. Nilai hasil operasi perbandingan tersebut kemudian disimpan dalam variabel isEqual.

Untuk memunculkan nilai bool menggunakan fmt.Printf(), bisa gunakan layout format %t.

Operator Logika

Operator ini digunakan untuk mencari benar tidaknya kombinasi data bertipe bool (bisa berupa variabel bertipe bool, atau hasil dari operator perbandingan).

Beberapa operator logika standar yang bisa digunakan :

TandaPenjelasan
&&kiri dan kanan
||kiri atau kanan
!negasi / nilai kebalikan

Contoh penggunaan :

				
					var left = false
var right = true

var leftAndRight = left && right
fmt.Printf("left && right \t(%t) \n", leftAndRight)

var leftOrRight = left || right
fmt.Printf("left || right \t(%t) \n", leftOrRight)

var leftReverse = !left
fmt.Printf("!left \t\t(%t) \n", leftReverse)
				
			

Hasil dari operator logika sama dengan hasil dari operator perbandingan, yaitu berupa boolean.

Berikut penjelasan statemen operator logika pada kode di atas.

  • leftAndRight bernilai false, karena hasil dari false dan true adalah false.
  • leftOrRight bernilai true, karena hasil dari false atau true adalah true.
  • leftReverse bernilai true, karena negasi (atau lawan dari) false adalah true.

Template \t digunakan untuk menambahkan indent tabulasi. Biasa dimanfaatkan untuk merapikan tampilan output pada console.

Seleksi Kondisi

Go memiliki 2 macam keyword untuk seleksi kondisi, yaitu if else dan switch. Pada chapter ini kita akan mempelajarinya satu-persatu.

Untuk catatan : Go tidak mendukung seleksi kondisi menggunakan ternary.
Statement seperti: var data = (isExist ? “ada” : “tidak ada”) adalah invalid dan menghasilkan error.

if, else if, & else

Cara penerapan if-else di Go sama seperti pada bahasa pemrograman lain. Yang membedakan hanya tanda kurungnya (parentheses), di Go tidak perlu ditulis. Kode berikut merupakan contoh penerapan seleksi kondisi if else, dengan jumlah kondisi 4 buah.

				
					var point = 8

if point == 10 {
    fmt.Println("lulus dengan nilai sempurna")
} else if point > 5 {
    fmt.Println("lulus")
} else if point == 4 {
    fmt.Println("hampir lulus")
} else {
    fmt.Printf("tidak lulus. nilai anda %d\n", point)
}
				
			

Dari ke-empat kondisi di atas, yang terpenuhi adalah if point > 5, karena nilai variabel point memang lebih besar dari 5. Maka blok kode tepat di bawah kondisi tersebut akan dieksekusi (blok kode ditandai kurung kurawal buka dan tutup), hasilnya text “lulus” muncul sebagai output.

Skema if else Go sama seperti pada pemrograman umumnya. Yaitu di awal seleksi kondisi menggunakan if, dan ketika kondisinya tidak terpenuhi akan menuju ke else (jika ada). Ketika ada banyak kondisi, gunakan else if.

Catatan : Di bahasa pemrograman lain, ketika ada seleksi kondisi yang isi blok-nya hanya 1 baris saja, kurung kurawal boleh tidak dituliskan. Berbeda dengan aturan di Go, kurung kurawal harus tetap dituliskan meski isinya hanya 1 blok satement.
Variabel Temporary Pada if - else

Variabel temporary adalah variabel yang hanya bisa digunakan pada blok seleksi kondisi di mana ia ditempatkan saja. Penggunaan variabel ini membawa beberapa manfaat, antara lain:

  • Scope atau cakupan variabel jelas, hanya bisa digunakan pada blok seleksi kondisi itu saja
  • Kode menjadi lebih rapi
  • Ketika nilai variabel tersebut didapat dari sebuah komputasi, perhitungan tidak perlu dilakukan di dalam blok masing-masing kondisi.
				
					var point = 8840.0

if percent := point / 100; percent >= 100 {
    fmt.Printf("%.1f%s perfect!\n", percent, "%")
} else if percent >= 70 {
    fmt.Printf("%.1f%s good\n", percent, "%")
} else {
    fmt.Printf("%.1f%s not bad\n", percent, "%")
}
				
			

Variabel percent nilainya didapat dari hasil perhitungan, dan hanya bisa digunakan di deretan blok seleksi kondisi itu saja.

Deklarasi variabel temporary hanya bisa dilakukan lewat metode type inference yang menggunakan tanda :=. Penggunaan keyword var di situ tidak diperbolehkan karena akan menyebabkan error.
Seleksi Kondisi Menggunakan Keyword switch - case

Switch merupakan seleksi kondisi yang sifatnya fokus pada satu variabel, lalu kemudian di-cek nilainya. Contoh sederhananya seperti penentuan apakah nilai variabel x adalah: 1, 2, 3, atau lainnya.

				
					var point = 6

switch point {
case 8:
    fmt.Println("perfect")
case 7:
    fmt.Println("awesome")
default:
    fmt.Println("not bad")
}
				
			

Pada kode di atas, tidak ada kondisi atau case yang terpenuhi karena nilai variabel point tetap 6. Ketika hal seperti ini terjadi, blok kondisi default dipanggil. Bisa dibilang bahwa default merupakan else dalam sebuah switch.

Perlu diketahui, switch pada pemrograman Go memiliki perbedaan dibanding bahasa lain. Di Go, ketika sebuah case terpenuhi, tidak akan dilanjutkan ke pengecekan case selanjutnya, meskipun tidak ada keyword break di situ. Konsep ini berkebalikan dengan switch pada umumnya, yang ketika sebuah case terpenuhi, maka akan tetap dilanjut mengecek case selanjutnya kecuali ada keyword break.
Perulangan

Di Go keyword perulangan hanya for saja, tetapi meski demikian, kemampuannya merupakan gabungan for, foreach, dan while ibarat bahasa pemrograman lain.

Perulangan Menggunakan Keyword for

Ada beberapa cara standar menggunakan for. Cara pertama dengan memasukkan variabel counter perulangan beserta kondisinya setelah keyword. Perhatikan dan praktekan kode berikut.

				
					for i := 0; i < 5; i++ {
    fmt.Println("Angka", i)
}
				
			

Perulangan di atas hanya akan berjalan ketika variabel i bernilai di bawah 5, dengan ketentuan setiap kali perulangan, nilai variabel i akan di-iterasi atau ditambahkan 1 (i++ artinya ditambah satu, sama seperti i = i + 1). Karena i pada awalnya bernilai 0, maka perulangan akan berlangsung 5 kali, yaitu ketika i bernilai 0, 1, 2, 3, dan 4.

Penggunaan Keyword for Tanpa Argumen

Cara ke-2 adalah dengan menuliskan kondisi setelah keyword for (hanya kondisi). Deklarasi dan iterasi variabel counter tidak dituliskan setelah keyword, hanya kondisi perulangan saja. Konsepnya mirip seperti while milik bahasa pemrograman lain.

Kode berikut adalah contoh for dengan argumen hanya kondisi (seperti if), output yang dihasilkan sama seperti penerapan for cara pertama.

				
					var i = 0

for i < 5 {
    fmt.Println("Angka", i)
    i++
}
				
			
Pointer

Pointer adalah reference atau alamat memori. Variabel pointer berarti variabel yang berisi alamat memori suatu nilai. Sebagai contoh sebuah variabel bertipe integer memiliki nilai 4, maka yang dimaksud pointer adalah alamat memori di mana nilai 4 disimpan, bukan nilai 4 itu sendiri.

Variabel-variabel yang memiliki reference atau alamat pointer yang sama, saling berhubungan satu sama lain dan nilainya pasti sama. Ketika ada perubahan nilai, maka akan memberikan efek kepada variabel lain (yang referensi-nya sama) yaitu nilainya ikut berubah.

Penerapan Pointer

Variabel bertipe pointer ditandai dengan adanya tanda asterisk (*) tepat sebelum penulisan tipe data ketika deklarasi.

				
					var number *int
var name *string
				
			

Nilai default variabel pointer adalah nil (kosong). Variabel pointer tidak bisa menampung nilai yang bukan pointer, dan sebaliknya variabel biasa tidak bisa menampung nilai pointer.

Ada dua hal penting yang perlu diketahui mengenai pointer:

  • Variabel biasa bisa diambil nilai pointernya, caranya dengan menambahkan tanda ampersand (&) tepat sebelum nama variabel. Metode ini disebut dengan referencing.
  • Dan sebaliknya, nilai asli variabel pointer juga bisa diambil, dengan cara menambahkan tanda asterisk (*) tepat sebelum nama variabel. Metode ini disebut dengan dereferencing.
				
					package main

import (
	"fmt"
)

func main() {
	var numberA int = 4
	var numberB *int = &numberA
	fmt.Println("numberA (value)   :", numberA)  // 4
	fmt.Println("numberA (address) :", &numberA) // 0xc20800a220

	fmt.Println("numberB (value)   :", *numberB) // 4
	fmt.Println("numberB (address) :", numberB)  // 0xc20800a220
}

				
			
				
					numberA (value)   : 4
numberA (address) : 0xc00000e0a8
numberB (value)   : 4
numberB (address) : 0xc00000e0a8
				
			

Variabel numberB dideklarasikan bertipe pointer int dengan nilai awal adalah referensi variabel numberA (bisa dilihat pada kode &numberA). Dengan ini, variabel numberA dan numberB menampung data dengan referensi alamat memori yang sama.

Variabel pointer jika di-print akan menghasilkan string alamat memori (dalam notasi heksadesimal), contohnya seperti numberB yang diprint menghasilkan 0xc20800a220.

Nilai asli sebuah variabel pointer bisa didapatkan dengan cara di-dereference terlebih dahulu (bisa dilihat pada kode *numberB).

Efek Perubahan Nilai Pointer

Ketika salah satu variabel pointer di ubah nilainya, sedang ada variabel lain yang memiliki referensi memori yang sama, maka nilai variabel lain tersebut juga akan berubah.

				
					var numberA int = 4
var numberB *int = &numberA

fmt.Println("numberA (value)   :", numberA)
fmt.Println("numberA (address) :", &numberA)
fmt.Println("numberB (value)   :", *numberB)
fmt.Println("numberB (address) :", numberB)

fmt.Println("")

numberA = 5

fmt.Println("numberA (value)   :", numberA)
fmt.Println("numberA (address) :", &numberA)
fmt.Println("numberB (value)   :", *numberB)
fmt.Println("numberB (address) :", numberB)
				
			

Variabel numberA dan numberB memiliki referensi memori yang sama. Perubahan pada salah satu nilai variabel tersebut akan memberikan efek pada variabel lainnya. Pada contoh di atas, numberA nilainya di ubah menjadi 5. membuat nilai asli variabel numberB ikut berubah menjadi 5.

				
					numberA (value)   : 4
numberA (address) : 0xc000122058
numberB (value)   : 4
numberB (address) : 0xc000122058

numberA (value)   : 5
numberA (address) : 0xc000122058
numberB (value)   : 5
numberB (address) : 0xc000122058
				
			
Parameter Pointer

Parameter bisa juga dirancang sebagai pointer. Cara penerapannya kurang lebih sama, dengan cara mendeklarasikan parameter sebagai pointer.

				
					package main

import "fmt"

func main() {
    var number = 4
    fmt.Println("before :", number) // 4

    change(&number, 10)
    fmt.Println("after  :", number) // 10
}

func change(original *int, value int) {
    *original = value
}
				
			

Fungsi change() memiliki 2 parameter, yaitu original yang tipenya adalah pointer int, dan value yang bertipe int. Di dalam fungsi tersebut nilai asli parameter pointer original diubah.

Fungsi change() kemudian diimplementasikan di main. Variabel number yang nilai awalnya adalah 4 diambil referensi-nya lalu digunakan sebagai parameter pada pemanggilan fungsi change().

Nilai variabel number berubah menjadi 10 karena perubahan yang terjadi di dalam fungsi change adalah pada variabel pointer.

				
					before : 4
after  : 10
				
			
Properti Public dan Private (Exported vs Unexported)

Di Go sebenarnya tidak ada istilah public modifier dan private modifier. Yang ada adalah exported yang kalau di bahasa lain ekuivalen dengan public modifier, dan unexported untuk private modifier.

Pengembangan aplikasi dalam real development pasti membutuhkan banyak sekali file program. Tidak mungkin dalam sebuah project semua file memiliki nama package main, biasanya akan dipisah sebagai package berbeda sesuai bagiannya.

Project folder selain berisikan file-file .go juga bisa berisikan sub-folder lainnya. Di Go, setiap folder atau sub-folder adalah satu package, file-file yang ada di dalam sebuah folder package-nya harus sama. Dan package pada file-file tersebut harus berbeda dengan package pada file-file lainnya yang berada pada folder berbeda.

Jadi mudahnya, 1 folder adalah 1 package.

Dalam sebuah package, biasanya kita menulis sangat banyak komponen, entah itu fungsi, struct, variabel, atau lainnya. Komponen tersebut bisa leluasa digunakan dalam package yang sama. Contoh sederhananya seperti program yang telah kita praktekan pada chapter sebelum-sebelumnya, dalam package main ada banyak yang di-define: fungsi, variabel, closure, struct, dan lainnya; ke semuanya bisa langsung dimanfaatkan.

Jika dalam satu program terdapat lebih dari 1 package, atau ada package lain selain main, maka komponen dalam package lain tersebut tidak bisa diakses secara bebas dari file yang package-nya main, karena tiap komponen memiliki hak akses.

Ada 2 jenis hak akses di Go:

  • Hak akses Exported atau public. Menandakan komponen tersebut diperbolehkan untuk diakses dari package lain yang berbeda
  • Hak akses Unexported atau private. Berarti komponen hanya bisa diakses dalam package yang sama, bisa dalam satu file saja atau dalam beberapa file yang masih 1 folder.

Penentuan hak akses yang tepat untuk tiap komponen sangatlah penting.

Di Go cara menentukan level akses atau modifier sangat mudah, penandanya adalah character case huruf pertama nama fungsi, struct, variabel, atau lainnya. Ketika namanya diawali dengan huruf kapital menandakan kalau exported (atau public). Dan sebaliknya, jika diawali huruf kecil, berarti unexported (atau private).

Misal dalam sebuah project terdapat beberapa library

my-app
  –  go.mod
  –  go.sum
  –  main.go
  –  libraries
  – –  librarysatu.go
  – –  librarydua.go
  –  partial.go

libraries/librarysatu.go

				
					package libraries

import "fmt"

func SayHello() {
    fmt.Println("hello")
}

func introduce(name string) {
    fmt.Println("nama saya", name)
}
				
			

File library.go yang telah dibuat ditentukan nama package-nya adalah library (sesuai dengan nama folder), berisi dua buah fungsi, SayHello() dan introduce().

  • Fungsi SayHello(), level aksesnya adalah publik, ditandai dengan nama fungsi diawali huruf besar.
  • Fungsi introduce() dengan level akses private, ditandai oleh huruf kecil di awal nama fungsi.

Selanjutnya kita lakukan tes apakah memang fungsi yang ber-modifier private dalam package library tidak bisa diakses dari package lain.

Buka file main.go, lalu tulis kode berikut.

				
					package main

import "example.com/library"

func main() {
    library.SayHello()
    library.introduce("ethan")
}
				
			

Sebenarnya dari Visual Studio Code sudah terlihat error, dan jika dijalankan

				
					.\main.go:13:11: undefined: examples.introduce
.\main.go:13:21: more than one character in rune literal
				
			
Penggunaan Hak Akses Exported dan Unexported pada Struct dan Propertinya

Level akses exported (atau public) dan unexported (atau private) juga bisa diterapkan di fungsi, struct, method, maupun property variabel. Cara penggunaannya sama seperti pada pembahasan sebelumnya, yaitu dengan menentukan character case huruf pertama nama komponen, apakah huruf besar atau kecil.

Belajar tentang level akses di Go akan lebih cepat jika langsung praktek. Oleh karena itu langsung saja. Hapus isi file library.go, buat struct baru dengan nama student di dalamnya. Contohnya seperti ini.

				
					type Student struct { //student unexported
    Name  string //name unexported
    Grade int
}
				
			

Selain nama struct-nya harus berbentuk exported, properti yang diakses juga harus exported juga.

Import Dengan Prefix Tanda Titik

Di Go, komponen yang berada di package lain yang di-import bisa dijadikan se-level dengan komponen package peng-import, caranya dengan menambahkan tanda titik (.) setelah penulisan keyword import. Maksud dari se-level di sini adalah, semua properti di package lain yg di-import bisa diakses tanpa perlu menuliskan nama package, seperti ketika mengakses sesuatu dari file yang sama.

				
					import (
    . "example.com/library"
    "fmt"
)

func main() {
    SayHello() // tadinya library.SayHello()
}
				
			
Pemanfaatan Alias Ketika Import Package

Fungsi yang berada di package lain bisa diakses dengan cara menuliskan nama-package diikuti nama fungsi-nya, contohnya seperti fmt.Println(). Package yang sudah di-import tersebut bisa diubah namanya dengan cara menggunakan alias pada saat import. Contohnya bisa dilihat pada kode berikut.

				
					import (
    f "fmt"
)

func main() {
    f.Println("Hello World!")
}
				
			

Pada kode di-atas, package fmt di tentukan aliasnya adalah f, untuk mengakses Println() cukup dengan f.Println().

Mengakses Properti Dalam File Yang Package-nya Sama

Jika properti yang ingin di akses masih dalam satu package tapi berbeda file, cara mengaksesnya bisa langsung dengan memanggil namanya. Hanya saja ketika eksekusi, file-file lain yang yang nama package-nya sama juga ikut dipanggil.

Langsung saja kita praktekan, buat file baru dalam my-app dengan nama partial.go.

				
					package main

import "fmt"

func sayHelloPartial(name string) string {
	return fmt.Sprintf("halo %s from partial", name)
}

				
			

Hapus semua isi file main.go, lalu silakan tulis kode berikut.

				
					package main

func main() {
    sayHelloPartial("ethan")
}
				
			

Sekarang terdapat 2 file berbeda (main.go dan partial.go) dengan package adalah sama, main. Pada saat go build atau go run, semua file dengan nama package main harus dituliskan sebagai argumen command.

				
					go run main.go partial.go
//atau
go run .
				
			

Fungsi sayHello pada file partial.go bisa dikenali meski level aksesnya adalah unexported. Hal ini karena kedua file tersebut (main.go dan partial.go) memiliki package yang sama.

Fungsi init()

Selain fungsi main(), terdapat juga fungsi spesial, yaitu init(). Fungsi ini otomatis dipanggil pertama kali ketika aplikasi di-run. Jika fungsi ini berada dalam package main, maka dipanggil lebih dulu sebelum fungsi main() dieksekusi.

Langsung saja kita praktekkan. Buka file librarysatu.go, hapus isinya lalu isi dengan kode berikut.

				
					package libraries

import "fmt"

var Student = struct {
    Name  string
    Grade int
}{}

func init() {
    Student.Name = "John Wick"
    Student.Grade = 2

    fmt.Println("--> library/library.go imported")
}
				
			

Pada package tersebut, variabel Student dibuat dengan isi anonymous struct. Dalam fungsi init, nilai Name dan Grade variabel di-set.

Selanjutnya buka file main.go, isi dengan kode berikut.

				
					package main

import "example.com/library"
import "fmt"

func main() {
    fmt.Printf("Name  : %s\n", library.Student.Name)
    fmt.Printf("Grade : %d\n", library.Student.Grade)
}
				
			

Package library di-import, dan variabel Student dikonsumsi. Pada saat import package, fungsi init() yang berada di dalamnya langsung dieksekusi.

Property variabel objek Student akan diisi dan sebuah pesan ditampilkan ke console.

Dalam sebuah package diperbolehkan ada banyak fungsi init() (urutan eksekusinya adalah sesuai file mana yg terlebih dahulu digunakan). Fungsi ini dipanggil sebelum fungsi main(), pada saat eksekusi program.

				
					--> libraries/library.go imported
Name  : John Wick
Grade : 2
				
			
Defer & Exit

Defer digunakan untuk mengakhirkan eksekusi sebuah statement tepat sebelum blok fungsi selesai. Sedangkan Exit digunakan untuk menghentikan program secara paksa (ingat, menghentikan program, tidak seperti return yang hanya menghentikan blok kode).

Penerapan keyword defer

Seperti yang sudah dijelaskan secara singkat di atas, bahwa defer digunakan untuk mengakhirkan eksekusi baris kode dalam skope blok fungsi. Ketika eksekusi blok sudah hampir selesai, statement yang di-defer dijalankan.

Defer bisa ditempatkan di mana saja, awal maupun akhir blok. Tetapi tidak mempengaruhi kapan waktu dieksekusinya, akan selalu dieksekusi di akhir.

				
					package main

import "fmt"

func main() {
    defer fmt.Println("halo")
    fmt.Println("selamat datang")
}
				
			
				
					selamat datang
halo
				
			

Keyword defer di atas akan mengakhirkan ekseusi fmt.Println("halo"), efeknya pesan "halo" akan muncul setelah "selamat datang".

Statement yang di-defer akan tetap muncul meskipun blok kode diberhentikan ditengah jalan menggunakan return. Contohnya seperti pada kode berikut.

				
					func main() {
    orderSomeFood("pizza")
    orderSomeFood("burger")
}

func orderSomeFood(menu string) {
    defer fmt.Println("Terimakasih, silakan tunggu")
    if menu == "pizza" {
        fmt.Print("Pilihan tepat!", " ")
        fmt.Print("Pizza ditempat kami paling enak!", "\n")
        return
    }

    fmt.Println("Pesanan anda:", menu)
}
				
			
				
					Pilihan tepat! Pizza ditempat kami paling enak!
Terimakasih, silakan tunggu
Pesanan anda: burger
Terimakasih, silakan tunggu
				
			

Info tambahan, ketika ada banyak statement yang di-defer, maka seluruhnya akan dieksekusi di akhir secara berurutan.

Kombinasi defer dan IIFE

Eksekusi defer adalah di akhir blok fungsi, bukan blok lainnya seperti blok seleksi kondisi.

				
					func main() {
    number := 3

    if number == 3 {
        fmt.Println("halo 1")
        defer fmt.Println("halo 3")
    }

    fmt.Println("halo 2")
}
				
			
				
					halo 1
halo 2
halo 3
				
			

Pada contoh di atas halo 3 akan tetap di print setelah halo 2 meskipun statement defer dipergunakan dalam blok seleksi kondisi if. Hal ini karena defer eksekusinya terjadi pada akhir blok fungsi (dalam contoh di atas main()), bukan pada akhir blok if.

Agar halo 3 bisa dimunculkan di akhir blok if, maka harus dibungkus dengan IIFE. Contoh:

				
					func main() {
    number := 3

    if number == 3 {
        fmt.Println("halo 1")
        func() {
            defer fmt.Println("halo 3")
        }()
    }

    fmt.Println("halo 2")
}
				
			
				
					halo 1
halo 3
halo 2
				
			

Bisa dilihat halo 3 muncul sebelum halo 2, karena dalam blok seleksi kondisi if eksekusi defer terjadi dalam blok fungsi anonymous (IIFE).

Penerapan Fungsi os.Exit()

Exit digunakan untuk menghentikan program secara paksa pada saat itu juga. Semua statement setelah exit tidak akan dieksekusi, termasuk juga defer.

Fungsi os.Exit() berada dalam package os. Fungsi ini memiliki sebuah parameter bertipe numerik yang wajib diisi. Angka yang dimasukkan akan muncul sebagai exit status ketika program berhenti.

				
					package main

import "fmt"
import "os"

func main() {
    defer fmt.Println("halo")
    os.Exit(1)
    fmt.Println("selamat datang")
}
				
			

Meskipun defer fmt.Println("halo") ditempatkan sebelum os.Exit(), statement tersebut tidak akan dieksekusi, karena di-tengah fungsi program dihentikan secara paksa.

				
					exit status 1