Pemrograman Go dari Nol dengan 25 Contoh

Go dari Nol dengan 25 Contoh

Go (Golang) adalah bahasa yang dikembangkan di Google. Bahasa ini sederhana, cepat dikompilasi, dan mendukung konkurensi secara bawaan, sehingga populer untuk server, perkakas, dan sistem cloud. Artikel ini memiliki 25 contoh kode, yang masing-masing dijalankan dengan Go 1.27, dengan keluaran sebenarnya dari setiap contoh. Contohnya bergerak dari dasar sampai konkurensi, handler web, dan generics.

Yang Anda butuhkan

Pasang Go dari go.dev/dl lalu periksa dengan `go version`. Buat berkas `.go` dan jalankan dengan `go run namafile.go`.

Ilustrasi

Diagram di bawah menunjukkan alur kerja dan gagasan goroutine dengan channel (gambar skematik, bukan tangkapan layar sebenarnya).
Diagram di bawah menunjukkan alur kerja dan gagasan goroutine dengan channel (gambar skematik, bukan tangkapan layar sebenarnya).

Contoh 1: Halo, dunia

Program Go paling sederhana. Setiap program dimulai dengan paket main dan fungsi main.

package main

import "fmt"

func main() {
	fmt.Println("Halo Go")
}

Keluaran sebenarnya

Halo Go

Contoh 2: Variabel dan konstanta

`:=` mendeklarasikan dan memberi nilai dalam satu langkah, `var` mendeklarasikan secara eksplisit, dan `const` untuk nilai yang tidak pernah berubah.

package main

import "fmt"

const schoolName = "Sekolah Contoh"

func main() {
	name := "Budi"
	var age int = 20
	fmt.Println(schoolName, name, age)
}

Keluaran sebenarnya

Sekolah Contoh Budi 20

Contoh 3: Nilai nol

Variabel yang dideklarasikan tanpa nilai akan mendapat nilai nol dari tipenya, seperti 0, string kosong, atau false.

package main

import "fmt"

func main() {
	var n int
	var s string
	var b bool
	var f float64
	fmt.Printf("int=%d string=%q bool=%t float=%.1f\n", n, s, b, f)
}

Keluaran sebenarnya

int=0 string="" bool=false float=0.0

Contoh 4: if dan else

Pengambilan keputusan dengan if dan else if. Go tidak memerlukan tanda kurung di sekitar kondisi.

package main

import "fmt"

func main() {
	score := 72
	if score >= 80 {
		fmt.Println("A")
	} else if score >= 70 {
		fmt.Println("B")
	} else {
		fmt.Println("C")
	}
}

Keluaran sebenarnya

B

Contoh 5: Perulangan for

Go hanya memiliki satu kata kunci perulangan, yaitu for, yang bisa menghitung atau bertindak seperti while.

package main

import "fmt"

func main() {
	for i := 1; i <= 3; i++ {
		fmt.Println("hitung", i)
	}
	n := 0
	for n < 2 {
		n++
	}
	fmt.Println("n =", n)
}

Keluaran sebenarnya

hitung 1
hitung 2
hitung 3
n = 2

Contoh 6: switch

switch membandingkan beberapa kasus dan tidak lanjut ke kasus berikutnya kecuali Anda memintanya.

package main

import "fmt"

func main() {
	day := "Sabtu"
	switch day {
	case "Sabtu", "Minggu":
		fmt.Println("hari libur")
	default:
		fmt.Println("hari kerja")
	}
}

Keluaran sebenarnya

hari libur

Contoh 7: Array dan slice

Array memiliki ukuran tetap. Slice bertambah dengan append dan bisa dipotong dengan s[a:b].

package main

import "fmt"

func main() {
	arr := [3]int{1, 2, 3}
	s := []int{10, 20}
	s = append(s, 30, 40)
	fmt.Println("array:", arr, "len:", len(arr))
	fmt.Println("slice:", s, "len:", len(s), "cap:", cap(s) >= 4)
	fmt.Println("irisan:", s[1:3])
}

Keluaran sebenarnya

array: [1 2 3] len: 3
slice: [10 20 30 40] len: 4 cap: true
irisan: [20 30]

Contoh 8: Map

Map menyimpan pasangan kunci-nilai. Bentuk `v, ok :=` memeriksa apakah sebuah kunci ada.

package main

import "fmt"

func main() {
	scores := map[string]int{"Budi": 85, "Siti": 72}
	scores["Andi"] = 45
	if v, ok := scores["Budi"]; ok {
		fmt.Println("nilai:", v)
	}
	delete(scores, "Siti")
	fmt.Println("jumlah:", len(scores))
}

Keluaran sebenarnya

nilai: 85
jumlah: 2

Contoh 9: Fungsi dengan beberapa nilai kembali

Sebuah fungsi dapat mengembalikan beberapa nilai. Error biasanya dikembalikan sebagai nilai terakhir.

package main

import (
	"errors"
	"fmt"
)

func divide(a, b int) (int, error) {
	if b == 0 {
		return 0, errors.New("tidak bisa dibagi nol")
	}
	return a / b, nil
}

func main() {
	q, err := divide(10, 2)
	fmt.Println("hasil:", q, "error:", err)
	_, err = divide(1, 0)
	fmt.Println("error:", err)
}

Keluaran sebenarnya

hasil: 5 error: <nil>
error: tidak bisa dibagi nol

Contoh 10: Closure

Fungsi yang mengingat variabel dari lingkup di sekitarnya. Berguna untuk penghitung dan state pribadi.

package main

import "fmt"

func counter() func() int {
	count := 0
	return func() int {
		count++
		return count
	}
}

func main() {
	next := counter()
	fmt.Println(next(), next(), next())
}

Keluaran sebenarnya

1 2 3

Contoh 11: Struct dan method

Struct mengelompokkan beberapa data, dan method memberikan perilaku pada tipe tersebut.

package main

import "fmt"

type Student struct {
	Name  string
	Score int
}

func (s Student) Grade() string {
	if s.Score >= 80 {
		return "A"
	}
	return "B"
}

func main() {
	s := Student{Name: "Budi", Score: 85}
	fmt.Println(s.Name, s.Grade())
}

Keluaran sebenarnya

Budi A

Contoh 12: Interface

Interface mendaftar method yang harus dimiliki sebuah tipe. Tipe mana pun yang memiliki method tersebut otomatis memenuhi interface.

package main

import "fmt"

type Shape interface {
	Area() float64
}

type Rect struct{ W, H float64 }

func (r Rect) Area() float64 { return r.W * r.H }

func main() {
	var s Shape = Rect{W: 3, H: 4}
	fmt.Printf("luas = %.1f\n", s.Area())
}

Keluaran sebenarnya

luas = 12.0

Contoh 13: Pointer

&x memberikan alamat sebuah variabel dan *p membaca atau mengubah nilai di alamat itu, sehingga fungsi dapat mengubah data pemanggilnya.

package main

import "fmt"

func increment(n *int) {
	*n++
}

func main() {
	x := 5
	increment(&x)
	fmt.Println("x =", x)
}

Keluaran sebenarnya

x = 6

Contoh 14: Menangani error

Buat error dengan fmt.Errorf dan %w, lalu periksa dengan errors.Is untuk melihat apakah sebuah error cocok dengan error yang sudah dikenal.

package main

import (
	"errors"
	"fmt"
)

var ErrNotFound = errors.New("data tidak ditemukan")

func find(id int) error {
	if id != 1 {
		return fmt.Errorf("kode %d: %w", id, ErrNotFound)
	}
	return nil
}

func main() {
	err := find(2)
	fmt.Println(err)
	fmt.Println("cocok dengan ErrNotFound:", errors.Is(err, ErrNotFound))
}

Keluaran sebenarnya

kode 2: data tidak ditemukan
cocok dengan ErrNotFound: true

Contoh 15: defer

defer menunda pemanggilan sampai fungsi di sekitarnya selesai. Cocok untuk menutup berkas dan melepaskan sumber daya.

package main

import "fmt"

func main() {
	defer fmt.Println("dijalankan terakhir")
	fmt.Println("mulai")
	fmt.Println("memproses")
}

Keluaran sebenarnya

mulai
memproses
dijalankan terakhir

Contoh 16: panic dan recover

panic menghentikan eksekusi normal, dan recover hanya bisa menangkapnya di dalam fungsi yang di-defer, sehingga program tidak crash.

package main

import "fmt"

func safeDivide(a, b int) (result int, err error) {
	defer func() {
		if r := recover(); r != nil {
			err = fmt.Errorf("pulih dari panic: %v", r)
		}
	}()
	return a / b, nil
}

func main() {
	fmt.Println(safeDivide(10, 2))
	fmt.Println(safeDivide(1, 0))
}

Keluaran sebenarnya

5 <nil>
0 pulih dari panic: runtime error: integer divide by zero

Contoh 17: Goroutine

Kata kunci go memulai pekerjaan yang berjalan secara konkuren. sync.WaitGroup menunggu sampai semua pekerjaan selesai.

package main

import (
	"fmt"
	"sync"
)

func main() {
	var wg sync.WaitGroup
	for i := 1; i <= 3; i++ {
		wg.Add(1)
		go func(n int) {
			defer wg.Done()
			_ = n * n
		}(i)
	}
	wg.Wait()
	fmt.Println("semua tugas selesai")
}

Keluaran sebenarnya

semua tugas selesai

Contoh 18: Channel

Channel mengirim nilai di antara goroutine. Contoh ini mengirim nilai dari goroutine ke main, lalu menutup channel.

package main

import "fmt"

func main() {
	ch := make(chan int)
	go func() {
		for i := 1; i <= 3; i++ {
			ch <- i * 10
		}
		close(ch)
	}()
	for v := range ch {
		fmt.Println("menerima", v)
	}
}

Keluaran sebenarnya

menerima 10
menerima 20
menerima 30

Contoh 19: select dan timeout

select menunggu beberapa channel sekaligus. time.After menetapkan waktu tunggu maksimum.

package main

import (
	"fmt"
	"time"
)

func main() {
	result := make(chan string)
	go func() {
		time.Sleep(200 * time.Millisecond)
		result <- "selesai"
	}()
	select {
	case msg := <-result:
		fmt.Println(msg)
	case <-time.After(50 * time.Millisecond):
		fmt.Println("waktu habis")
	}
}

Keluaran sebenarnya

waktu habis

Contoh 20: Mutex untuk data bersama

sync.Mutex memungkinkan banyak goroutine mengubah satu variabel dengan aman. Totalnya selalu benar.

package main

import (
	"fmt"
	"sync"
)

func main() {
	var mu sync.Mutex
	var wg sync.WaitGroup
	total := 0
	for i := 0; i < 100; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			mu.Lock()
			total++
			mu.Unlock()
		}()
	}
	wg.Wait()
	fmt.Println("total =", total)
}

Keluaran sebenarnya

total = 100
Diagram di bawah menunjukkan alur kerja dan gagasan goroutine dengan channel (gambar skematik, bukan tangkapan layar sebenarnya).
Diagram di bawah menunjukkan alur kerja dan gagasan goroutine dengan channel (gambar skematik, bukan tangkapan layar sebenarnya).

Contoh 21: Paket strings dan rune

strings menyediakan fungsi teks. len menghitung byte, jadi hitung karakter non-ASCII dengan mengubah ke []rune.

package main

import (
	"fmt"
	"strings"
)

func main() {
	s := "Go mudah dipelajari"
	fmt.Println(strings.ToUpper("go"), strings.Contains(s, "mudah"))
	fmt.Println(strings.Split("a,b,c", ","), len("café"), len([]rune("café")))
	fmt.Println([]rune("café")[3] == 'é')
}

Keluaran sebenarnya

GO true
[a b c] 5 4
true

Contoh 22: Membaca dan menulis berkas

os.WriteFile dan os.ReadFile menulis dan membaca seluruh berkas dalam satu pemanggilan.

package main

import (
	"fmt"
	"os"
)

func main() {
	if err := os.WriteFile("note.txt", []byte("catatan dari Go\n"), 0644); err != nil {
		fmt.Println("gagal menulis:", err)
		return
	}
	data, err := os.ReadFile("note.txt")
	if err != nil {
		fmt.Println("gagal membaca:", err)
		return
	}
	fmt.Print("terbaca: ", string(data))
}

Keluaran sebenarnya

terbaca: catatan dari Go

Contoh 23: JSON

encoding/json mengubah struct menjadi JSON dan sebaliknya. Tag seperti `json:"..."` menetapkan nama field.

package main

import (
	"encoding/json"
	"fmt"
)

type Student struct {
	Name  string `json:"name"`
	Score int    `json:"score"`
}

func main() {
	b, _ := json.Marshal(Student{Name: "Budi", Score: 85})
	fmt.Println(string(b))

	var s Student
	_ = json.Unmarshal([]byte(`{"name":"Siti","score":72}`), &s)
	fmt.Println(s.Name, s.Score)
}

Keluaran sebenarnya

{"name":"Budi","score":85}
Siti 72

Contoh 24: Menguji handler HTTP

httptest menguji fungsi handler tanpa menjalankan server sungguhan.

package main

import (
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
)

func hello(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, "Halo dari Go HTTP")
}

func main() {
	req := httptest.NewRequest(http.MethodGet, "/", nil)
	rec := httptest.NewRecorder()
	hello(rec, req)
	body, _ := io.ReadAll(rec.Result().Body)
	fmt.Println(rec.Code, string(body))
}

Keluaran sebenarnya

200 Halo dari Go HTTP

Contoh 25: Generics

Fungsi yang bekerja pada beberapa tipe dengan memakai parameter tipe T, yang dibatasi oleh interface.

package main

import "fmt"

type Number interface {
	~int | ~float64
}

func Sum[T Number](values []T) T {
	var total T
	for _, v := range values {
		total += v
	}
	return total
}

func main() {
	fmt.Println(Sum([]int{1, 2, 3}))
	fmt.Println(Sum([]float64{1.5, 2.5}))
}

Keluaran sebenarnya

6
4

Langkah berikutnya

Cobalah menulis server web yang menerima JSON dan menyimpannya ke berkas. Setelah itu, pelajari paket net/http dan context untuk timeout serta pembatalan.

Ringkasan

Go memiliki sintaks yang sederhana, memeriksa tipe saat kompilasi, menangani error sebagai nilai, dan mendukung konkurensi dengan goroutine dan channel, sehingga cocok untuk server dan beban kerja dengan banyak tugas.