cv.cm

Pemrograman · Bahasa pemrograman

Tutorial Go (Golang) untuk pemula

6 menit baca

Jawaban singkat

Go (sering disebut Golang) adalah bahasa terkompilasi yang sederhana dengan tipe statis dari Google, dibuat untuk build cepat, concurrency yang mudah, dan server yang andal. Instal Go, tulis package main, lalu jalankan dengan go run hello.go. Konsep intinya: sedikit tipe dasar, slice dan map, struct dengan method, interface implisit, goroutine dan channel untuk concurrency, serta error yang dikembalikan sebagai nilai biasa.

Buka Pemformat JSON

Apa itu Go dan untuk apa dipakai

Go dirancang di Google oleh Robert Griesemer, Rob Pike, dan Ken Thompson dan dirilis sebagai open source pada 2009. Go dikompilasi menjadi satu binary statis, startup-nya cepat, punya garbage collector, dan pustaka standar jaringan yang kaya. Go dipakai untuk infrastruktur cloud dan tool command line: Docker, Kubernetes, Terraform, dan Prometheus ditulis dalam Go, dan banyak perusahaan memakainya untuk web API dan microservice. Bahasanya sengaja dibuat kecil, sehingga kode dari tim berbeda terlihat mirip.

Instal Go dan jalankan Hello, world

Unduh Go dari go.dev/dl dan periksa dengan go version. Buat folder, mulai module, dan tambahkan main.go:

package main

import "fmt"

func main() {
    fmt.Println("Hello, world")
}
go mod init example.com/hello
go run .
# Hello, world
go build     # produces a standalone binary named hello

Eksekusi dimulai dari func main di package main. Jalankan gofmt (atau go fmt) untuk memformat kode; semua kode Go memakai gaya yang sama, dengan tab untuk indentasi di file sumber sebenarnya.

Variabel dan tipe data dasar

Deklarasikan variabel dengan var name type = value, atau di dalam fungsi dengan bentuk singkat name := value, yang menyimpulkan tipenya. Variabel yang tidak diberi nilai mendapat zero value: 0, "", false, atau nil.

TipeContohZero value
int, int64n := 420
float64x := 3.140
boolok := truefalse
strings := "hi"""
runer := 'é'0 (sebuah code point Unicode)
byteb := byte(65)0 (alias untuk uint8)
package main

import (
    "fmt"
    "strconv"
)

func main() {
    name := "Ada"
    count := 7
    avg := float64(count) / 2 // explicit conversion is required
    fmt.Printf("%s has %d items, avg %.1f\n", name, count, avg) // Ada has 7 items, avg 3.5
    n, err := strconv.Atoi("12")
    fmt.Println(n+1, err) // 13 <nil>
}

Go tidak pernah mengonversi tipe numerik secara implisit, dan compiler menolak variabel serta import yang tidak terpakai.

Alur kontrol dan fungsi

Kondisi tidak diapit tanda kurung, dan kurung kurawal wajib. for adalah satu-satunya keyword perulangan: ia mencakup perulangan klasik, gaya while, dan range. Fungsi dapat mengembalikan beberapa nilai.

package main

import "fmt"

func divmod(a, b int) (int, int) {
    return a / b, a % b
}

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

    for i := 0; i < 3; i++ {
        fmt.Print(i, " ") // 0 1 2
    }
    fmt.Println()

    n := 10
    for n > 1 { // while-style loop
        n /= 2
    }

    q, r := divmod(17, 5)
    fmt.Println(n, q, r) // 1 3 2

    switch day := "SAT"; day {
    case "SAT", "SUN":
        fmt.Println("weekend") // no break needed
    default:
        fmt.Println("weekday")
    }
}

Slice, map, dan struct

Slice adalah tampilan array yang dapat tumbuh dan merupakan tipe list sehari-hari. Map adalah hash table. Struct mengelompokkan field bernama, dan method adalah fungsi dengan receiver.

package main

import (
    "fmt"
    "sort"
)

type Point struct {
    X, Y int
}

func (p Point) Add(o Point) Point { return Point{p.X + o.X, p.Y + o.Y} }

func (p *Point) Scale(k int) { p.X *= k; p.Y *= k } // pointer receiver modifies p

func main() {
    nums := []int{3, 1, 2}
    nums = append(nums, 5)
    sort.Ints(nums)
    fmt.Println(nums, len(nums), nums[1:3]) // [1 2 3 5] 4 [2 3]

    ages := map[string]int{"ann": 31, "bob": 25}
    ages["cy"] = 40
    age, ok := ages["zed"]
    fmt.Println(age, ok, len(ages)) // 0 false 3

    for i, n := range nums {
        fmt.Print(i, ":", n, " ") // 0:1 1:2 2:3 3:5
    }
    fmt.Println()

    p := Point{1, 2}.Add(Point{3, 4})
    p.Scale(2)
    fmt.Printf("%+v\n", p) // {X:8 Y:12}
}

Urutan iterasi pada map sengaja acak; urutkan key jika Anda butuh urutan yang stabil. Nama yang diawali huruf kapital (Point, Add) diekspor dari package.

Interface

Interface mendaftar signature method. Sebuah tipe memenuhi interface hanya dengan memiliki method tersebut; tidak ada keyword implements.

package main

import (
    "fmt"
    "math"
)

type Shape interface {
    Area() float64
}

type Circle struct{ R float64 }
type Rect struct{ W, H float64 }

func (c Circle) Area() float64 { return math.Pi * c.R * c.R }
func (r Rect) Area() float64   { return r.W * r.H }

func main() {
    shapes := []Shape{Circle{1}, Rect{2, 3}}
    total := 0.0
    for _, s := range shapes {
        total += s.Area()
    }
    fmt.Printf("%.2f\n", total) // 9.14
}

Go juga punya generic (sejak Go 1.18), misalnya func Max[T int | float64](a, b T) T, yang sebagian besar akan Anda pakai lewat fungsi pustaka seperti di package slices dan maps.

Fitur khas: goroutine dan channel

Goroutine adalah thread ringan yang dimulai dengan keyword go; satu program dapat menjalankan ribuan goroutine. Goroutine berkomunikasi lewat channel, yang mengirim nilai dengan aman di antara mereka. sync.WaitGroup menunggu sekelompok goroutine selesai.

package main

import (
    "fmt"
    "sync"
)

func square(n int, results chan<- int, wg *sync.WaitGroup) {
    defer wg.Done()
    results <- n * n
}

func main() {
    results := make(chan int, 5)
    var wg sync.WaitGroup
    for i := 1; i <= 5; i++ {
        wg.Add(1)
        go square(i, results, &wg)
    }
    wg.Wait()
    close(results)

    sum := 0
    for r := range results {
        sum += r
    }
    fmt.Println(sum) // 55
}

defer menjadwalkan sebuah pemanggilan agar dijalankan ketika fungsi di sekelilingnya selesai, dan begitulah kode Go menutup file dan membuka kunci mutex. Gunakan go run -race . untuk mendeteksi data race saat pengembangan.

Penanganan error

Go tidak memakai exception untuk kegagalan biasa. Fungsi yang bisa gagal mengembalikan error sebagai hasil terakhir, dan pemanggil langsung memeriksanya. Bungkus error dengan %w untuk menambah konteks sambil tetap membuat error asli bisa diperiksa dengan errors.Is.

package main

import (
    "errors"
    "fmt"
    "os"
)

func readConfig(path string) ([]byte, error) {
    data, err := os.ReadFile(path)
    if err != nil {
        return nil, fmt.Errorf("read config: %w", err)
    }
    return data, nil
}

func main() {
    _, err := readConfig("missing.json")
    if errors.Is(err, os.ErrNotExist) {
        fmt.Println("no config file:", err)
    }
}

panic dan recover ada, tetapi dicadangkan untuk kesalahan pemrograman dan kondisi yang benar-benar tak terduga, bukan untuk penanganan error biasa.

Program kecil yang lengkap: web server mini

Hanya dengan pustaka standar, program ini melayani JSON lewat HTTP. Jalankan dengan go run . dan buka http://localhost:8080/hello?name=Ann.

package main

import (
    "encoding/json"
    "log"
    "net/http"
    "time"
)

type Greeting struct {
    Message string    `json:"message"`
    Time    time.Time `json:"time"`
}

func hello(w http.ResponseWriter, r *http.Request) {
    name := r.URL.Query().Get("name")
    if name == "" {
        name = "world"
    }
    w.Header().Set("Content-Type", "application/json")
    err := json.NewEncoder(w).Encode(Greeting{"Hello, " + name, time.Now()})
    if err != nil {
        log.Println("encode:", err)
    }
}

func main() {
    http.HandleFunc("/hello", hello)
    log.Println("listening on :8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

Teks setelah tipe setiap field adalah struct tag yang menentukan nama key JSON. Setiap request ditangani di goroutine-nya sendiri secara otomatis.

Kesalahan umum pemula

  • Mengabaikan error dengan _. Periksa setiap error yang dikembalikan kecuali Anda yakin tidak berpengaruh.
  • Mengira append mengubah slice asli di tempat; selalu pakai hasilnya, seperti s = append(s, x).
  • Memulai goroutine lalu membiarkan main selesai sebelum mereka selesai. Tunggu dengan sync.WaitGroup atau channel.
  • Memakai value receiver padahal method harus mengubah struct; gunakan pointer receiver seperti func (p *Point).

Langkah selanjutnya

Ikuti Tour of Go yang interaktif, lalu baca Effective Go dan dokumentasi pustaka standar di pkg.go.dev. Pelajari go test untuk unit test dan benchmark, package context untuk timeout dan pembatalan, serta module untuk dependensi.

FAQ

Apakah namanya Go atau Golang?

Nama resminya Go. "Golang" berasal dari alamat situs lama golang.org dan masih dipakai sebagai kata pencarian.

Apakah Go berorientasi objek?

Sebagian. Go punya method dan interface, tetapi tidak ada class atau pewarisan; Anda membangun tipe dengan menyematkan (embedding) struct dan menyusun interface kecil.

Apakah Go lebih cepat daripada Python?

Untuk sebagian besar pekerjaan yang membebani CPU, ya, karena Go dikompilasi ke kode mesin native dan bertipe statis. Python tetap bisa cepat ketika pekerjaan berat terjadi di dalam pustaka yang ditulis dengan C.

Apa perbedaan goroutine dan thread?

Goroutine dikelola oleh runtime Go dan dimulai dengan stack kecil yang membesar sesuai kebutuhan. Runtime menjadwalkan banyak goroutine ke beberapa thread sistem operasi, sehingga goroutine jauh lebih murah dibuat.

Semua tutorial pemrograman