unisbadri.com » Python Java Golang Typescript Kotlin Ruby Rust Dart PHP

Tipe Data #

Memilih tipe data yang tepat bukan sekadar formalitas agar kode bisa dikompilasi. Pilihan antara int32 dan int64 menentukan apakah programmu akan overflow saat menghitung ID database yang besar. Pilihan antara float64 dan integer menentukan apakah kalkulasi keuanganmu akurat atau mengandung error pembulatan tersembunyi. Pilihan antara string dan []byte menentukan efisiensi manipulasi data biner. Go adalah bahasa statically typed — semua nilai punya tipe yang diketahui saat kompilasi — dan sistem tipenya dirancang agar pilihan yang salah mudah terdeteksi oleh compiler.

Kategori Tipe Data di Go #

Go membagi tipe data ke dalam beberapa kategori:

Tipe Data Go
│
├── Tipe Dasar (Basic Types)
│   ├── Numerik
│   │   ├── Integer: int, int8, int16, int32, int64
│   │   │            uint, uint8, uint16, uint32, uint64, uintptr
│   │   ├── Float:   float32, float64
│   │   └── Complex: complex64, complex128
│   ├── Boolean:     bool
│   └── String:      string
│
├── Tipe Komposit (Composite Types)
│   ├── Array:       [N]T
│   ├── Slice:       []T
│   ├── Map:         map[K]V
│   └── Struct:      struct { ... }
│
└── Tipe Referensi & Lainnya
    ├── Pointer:     *T
    ├── Function:    func(...)
    ├── Interface:   interface { ... }
    └── Channel:     chan T

Artikel ini fokus pada tipe dasar dan pointer. Tipe komposit (array, slice, map, struct) masing-masing punya artikel tersendiri karena kompleksitasnya.


Integer #

Go menyediakan integer dengan ukuran bit yang eksplisit. Ini berbeda dari banyak bahasa lain yang hanya punya satu tipe int — di Go kamu bisa (dan seringkali perlu) memilih ukuran yang tepat.

Signed Integer — Bisa Negatif #

var i8  int8  = 127              // -128 sampai 127
var i16 int16 = 32767            // -32,768 sampai 32,767
var i32 int32 = 2147483647       // sekitar ±2.1 miliar
var i64 int64 = 9223372036854775807  // sekitar ±9.2 kuintilion

// int — ukurannya mengikuti platform: 32-bit di sistem 32-bit, 64-bit di 64-bit
// Ini yang paling sering dipakai untuk keperluan umum
var i int = 100

Unsigned Integer — Hanya Non-Negatif #

var u8   uint8  = 255                    // 0 sampai 255
var u16  uint16 = 65535                  // 0 sampai 65,535
var u32  uint32 = 4294967295             // 0 sampai sekitar 4.3 miliar
var u64  uint64 = 18446744073709551615   // 0 sampai sekitar 18.4 kuintilion

// uint — mengikuti platform seperti int
var u uint = 100

// uintptr — menyimpan alamat memori, dipakai di kode tingkat rendah
var ptr uintptr

Panduan Memilih Tipe Integer #

Ini keputusan yang sering membingungkan pemula. Berikut panduan praktisnya:

GUNAKAN int (default) untuk:
  ✓ Index loop: for i := 0; i < n; i++
  ✓ Counter dan kuantitas umum
  ✓ Nilai yang tidak perlu ukuran spesifik

GUNAKAN int64 untuk:
  ✓ ID dari database (terutama auto-increment yang bisa sangat besar)
  ✓ Unix timestamp (detik sejak 1970): time.Now().Unix()
  ✓ Ukuran file dalam bytes (file besar bisa melebihi int32)
  ✓ Kalkulasi yang bisa menghasilkan angka sangat besar

GUNAKAN int32 untuk:
  ✓ Interoperabilitas dengan library C atau protokol jaringan yang pakai int32
  ✓ Protocol Buffers (protobuf) — tipe int32 di .proto file

GUNAKAN uint8 untuk:
  ✓ Data byte (alias: byte)
  ✓ Nilai pixel warna RGB (0-255)
  ✓ Data biner mentah

GUNAKAN uint16 untuk:
  ✓ Port number (0-65535)
  ✓ Karakter Unicode BMP

HINDARI unsigned untuk logika bisnis umum:
  ✗ Mudah terjadi underflow: uint(0) - 1 = 18446744073709551615 (bukan -1!)

Overflow Integer #

Overflow di Go tidak menyebabkan panic — ia “wrap around” secara silent:

var x int8 = 127
x++
fmt.Println(x)  // Output: -128  (bukan 128!)

var u uint8 = 0
u--
fmt.Println(u)  // Output: 255  (underflow, bukan -1!)
Integer overflow di Go tidak menyebabkan error runtime — nilai hanya “memutar” kembali ke ujung rentang yang lain. Ini bisa menjadi bug yang sangat sulit dideteksi. Untuk operasi yang berisiko overflow, selalu validasi rentang nilai sebelum operasi atau gunakan library safe math.

Float #

Go menyediakan dua tipe floating-point yang mengikuti standar IEEE 754:

var f32 float32 = 3.14           // ~7 digit desimal presisi
var f64 float64 = 3.14159265358979  // ~15-17 digit desimal presisi

// Default untuk literal desimal adalah float64
ratio := 1.5      // tipe: float64
pi    := 3.14159  // tipe: float64

float32 vs float64 — Kapan Pakai Mana? #

Dalam praktiknya, hampir selalu pakai float64. Alasannya: semua fungsi matematika di package math bekerja dengan float64, dan presisi float32 (7 digit) seringkali tidak cukup untuk kalkulasi yang panjang.

import "math"

// Semua fungsi math menggunakan float64
fmt.Println(math.Sqrt(2))      // 1.4142135623730951
fmt.Println(math.Sin(math.Pi)) // 1.2246467991473532e-16 (mendekati 0)
fmt.Println(math.Abs(-3.14))   // 3.14

// float32 hanya masuk akal untuk:
// - Grafis 3D (OpenGL, game engine) — GPU native float32
// - Dataset numerik sangat besar yang butuh hemat memori
// - Interop dengan library C yang pakai float

Masalah Presisi Floating-Point #

Ini bukan bug di Go — ini sifat fundamental representasi floating-point di semua bahasa:

fmt.Println(0.1 + 0.2)   // Output: 0.30000000000000004
fmt.Println(0.1 + 0.2 == 0.3)  // Output: false !

// Perbandingan float harus menggunakan epsilon (toleransi)
const epsilon = 1e-9
a := 0.1 + 0.2
b := 0.3
fmt.Println(math.Abs(a-b) < epsilon)  // Output: true

Jangan Gunakan Float untuk Uang #

Ini kesalahan yang sangat umum dan berpotensi menyebabkan kerugian nyata:

// ANTI-PATTERN: kalkulasi keuangan dengan float
harga := 9999.99
pajak := harga * 0.11  // 11%
total := harga + pajak
fmt.Printf("Total: %.2f\n", total)  // terlihat OK di output
// Tapi: harga*0.11 = 1099.9989000000001, bukan 1099.9989 persis

// BENAR: gunakan integer dalam unit terkecil (sen atau rupiah)
// Simpan semua harga dalam satuan sen/rupiah terkecil (integer)
hargaRupiah := 999999  // Rp 9.999,99 disimpan sebagai 999999 (dalam sen)
pajakSen := hargaRupiah * 11 / 100  // integer division
totalSen := hargaRupiah + pajakSen
fmt.Printf("Total: Rp %.2f\n", float64(totalSen)/100)

Untuk aplikasi keuangan produksi, pertimbangkan library seperti github.com/shopspring/decimal yang menyediakan tipe Decimal dengan presisi arbitrary.

Nilai Spesial Float #

import "math"

posInf := math.Inf(1)   // +Infinity
negInf := math.Inf(-1)  // -Infinity
nan    := math.NaN()    // Not a Number

fmt.Println(math.IsInf(posInf, 1))   // true
fmt.Println(math.IsNaN(nan))          // true
fmt.Println(nan == nan)               // false! NaN tidak sama dengan dirinya sendiri

Complex #

Tipe bilangan kompleks tersedia langsung di Go tanpa library tambahan:

var z1 complex64  = 3 + 4i
var z2 complex128 = 1.5 + 2.5i

// Atau gunakan fungsi complex()
z3 := complex(3.0, 4.0)  // 3+4i

// Mengakses bagian real dan imajiner
fmt.Println(real(z3))  // 3
fmt.Println(imag(z3))  // 4

// Operasi aritmatika
sum := z1 + complex64(z2)
fmt.Println(sum)  // (4.5+6.5i)

Tipe complex jarang dipakai dalam pengembangan aplikasi sehari-hari — paling relevan untuk komputasi ilmiah, pemrosesan sinyal digital (DSP), atau grafis komputer yang melibatkan transformasi Fourier.


Boolean #

bool hanya punya dua nilai: true dan false. Zero value-nya adalah false.

var aktif bool = true
var nonaktif bool   // zero value: false

// Hasil dari operasi perbandingan selalu bool
x := 42
fmt.Println(x > 10)    // true
fmt.Println(x == 10)   // false
fmt.Println(x != 42)   // false
fmt.Println(x >= 42)   // true

Operator Logika dan Short-Circuit Evaluation #

a, b := true, false

fmt.Println(a && b)   // false — AND: keduanya harus true
fmt.Println(a || b)   // true  — OR: salah satu cukup true
fmt.Println(!a)       // false — NOT: negasi

// Short-circuit evaluation — penting untuk keamanan
// Jika bagian kiri && sudah false, bagian kanan TIDAK dievaluasi
var p *int = nil
if p != nil && *p > 0 {  // aman: *p tidak dievaluasi jika p == nil
    fmt.Println("positif")
}

// Jika bagian kiri || sudah true, bagian kanan TIDAK dievaluasi
func isAdmin(user *User) bool {
    return user != nil && user.Role == RoleAdmin
}
Short-circuit evaluation bukan hanya optimasi performa — ini adalah pola keamanan penting di Go. Selalu taruh pengecekan nil atau kondisi “penjaga” di sisi kiri &&, dan kondisi yang mahal di sisi kanan yang hanya dievaluasi jika dibutuhkan.

String #

String di Go adalah sequence of bytes yang immutable — urutan byte yang tidak bisa diubah setelah dibuat. Ini berbeda dari banyak bahasa lain di mana string bisa dimodifikasi in-place.

s := "Hello, Go!"

// len() mengembalikan jumlah BYTES, bukan jumlah karakter
fmt.Println(len(s))   // 10

// Akses elemen menggunakan index — menghasilkan byte (uint8), bukan karakter
fmt.Println(s[0])         // 72 (nilai byte untuk 'H')
fmt.Println(string(s[0])) // "H"

// String tidak bisa dimodifikasi langsung
// s[0] = 'h'  // ← compile error: cannot assign to s[0]

// Untuk modifikasi, konversi ke []byte, modifikasi, konversi balik
b := []byte(s)
b[0] = 'h'
s2 := string(b)
fmt.Println(s2)  // "hello, Go!"

Raw String Literal #

Selain string biasa dengan tanda kutip ganda, Go mendukung raw string literal menggunakan backtick. Raw string tidak memproses escape sequence apapun:

// String biasa — escape sequence diproses
s1 := "baris pertama\nbaris kedua\ttab"

// Raw string literal — ditampilkan apa adanya, termasuk newline
s2 := `baris pertama
baris kedua
    tab literal`

// Sangat berguna untuk:
// - SQL query multi-baris
query := `
    SELECT u.id, u.name, u.email
    FROM users u
    JOIN orders o ON u.id = o.user_id
    WHERE u.active = true
    ORDER BY u.created_at DESC
    LIMIT 10
`

// - Regex pattern (tidak perlu escape backslash)
pattern := `^\d{4}-\d{2}-\d{2}$`   // raw: tanpa escape
// vs
pattern2 := "^\\d{4}-\\d{2}-\\d{2}$"  // harus escape backslash

// - JSON dalam test
jsonData := `{"name": "Budi", "age": 28}`

Operasi String dengan Package strings #

import "strings"

s := "  Hello, Go!  "

// Manipulasi dasar
fmt.Println(strings.ToUpper(s))          // "  HELLO, GO!  "
fmt.Println(strings.ToLower(s))          // "  hello, go!  "
fmt.Println(strings.TrimSpace(s))        // "Hello, Go!"
fmt.Println(strings.Trim(s, " "))        // "Hello, Go!"

// Pencarian
fmt.Println(strings.Contains(s, "Go"))   // true
fmt.Println(strings.HasPrefix(s, "  H")) // true
fmt.Println(strings.HasSuffix(s, "!  ")) // true
fmt.Println(strings.Index(s, "Go"))      // 9

// Transformasi
fmt.Println(strings.Replace(s, "Go", "Golang", 1)) // "  Hello, Golang!  "
fmt.Println(strings.ReplaceAll(s, " ", "_"))        // "__Hello,_Go!__"

// Split dan Join
parts := strings.Split("a,b,c,d", ",")  // ["a" "b" "c" "d"]
joined := strings.Join(parts, " | ")    // "a | b | c | d"
fields := strings.Fields("  foo bar  baz  ")  // ["foo" "bar" "baz"] (split whitespace)

// Konversi ke/dari angka — gunakan strconv
import "strconv"
n, err := strconv.Atoi("123")        // string → int
s3 := strconv.Itoa(456)              // int → string
f, err := strconv.ParseFloat("3.14", 64)  // string → float64
b2, err := strconv.ParseBool("true")      // string → bool

String Builder — Efisien untuk Konkatenasi Banyak String #

import "strings"

// ANTI-PATTERN: konkatenasi dengan + dalam loop — sangat tidak efisien
// Setiap += mengalokasikan string baru di memori
result := ""
for i := 0; i < 1000; i++ {
    result += fmt.Sprintf("item %d, ", i)  // O(n²) alokasi memori!
}

// BENAR: gunakan strings.Builder
var sb strings.Builder
for i := 0; i < 1000; i++ {
    fmt.Fprintf(&sb, "item %d, ", i)  // hanya satu alokasi di akhir
}
result2 := sb.String()

Byte dan Rune — Alias Tipe yang Penting #

byte — Alias dari uint8 #

byte adalah nama alias untuk uint8. Digunakan ketika konteksnya jelas bahwa nilai tersebut adalah data biner atau karakter ASCII:

var b byte = 'A'
fmt.Println(b)         // 65 (nilai ASCII)
fmt.Println(string(b)) // "A"

// Konversi string ke []byte untuk manipulasi binary
data := []byte("Hello")
data[0] = 'h'
fmt.Println(string(data))  // "hello"

rune — Alias dari int32 #

rune adalah nama alias untuk int32. Merepresentasikan satu Unicode code point — satu karakter Unicode. Ini penting karena karakter Unicode bisa membutuhkan lebih dari satu byte:

var r rune = '界'
fmt.Println(r)         // 30028 (Unicode code point U+754C)
fmt.Println(string(r)) // "界"

// String Unicode — perbedaan len dan jumlah karakter
s := "Hello, 世界"
fmt.Println(len(s))           // 13 (bytes, bukan karakter!)
fmt.Println(len([]rune(s)))   // 9  (karakter Unicode)

// Iterasi dengan range — per RUNE, bukan per byte
for i, r := range s {
    fmt.Printf("index %d: %c (U+%04X)\n", i, r, r)
}
// index 0: H (U+0048)
// index 1: e (U+0065)
// ...
// index 7: 世 (U+4E16)  ← index melompat ke 10 berikutnya karena 世 = 3 bytes
// index 10: 界 (U+754C)

// Akses karakter ke-N yang benar (bukan s[N])
runes := []rune(s)
fmt.Println(string(runes[7]))  // "世" — karakter ke-8 (index 7)

Pointer #

Pointer menyimpan alamat memori dari sebuah variabel. Go punya pointer, tapi jauh lebih aman dari C karena tidak ada pointer arithmetic — kamu tidak bisa melakukan ptr + 1 untuk pindah ke alamat berikutnya.

func main() {
    x := 42

    p := &x           // p adalah *int, menyimpan alamat x
    fmt.Println(p)    // Output: 0xc0000b4010 (contoh alamat)
    fmt.Println(*p)   // Output: 42  — dereference: baca nilai di alamat itu

    *p = 100          // tulis nilai baru ke alamat yang disimpan p
    fmt.Println(x)    // Output: 100  — x berubah!
}

Mengapa Pointer Diperlukan #

Tanpa pointer, semua nilai di Go di-pass by value — fungsi menerima salinan. Perubahan dalam fungsi tidak mempengaruhi variabel asli:

// ANTI-PATTERN: ingin modifikasi nilai asli tapi tidak pakai pointer
func doubleValue(n int) {
    n = n * 2  // hanya memodifikasi SALINAN lokal
}

func main() {
    x := 5
    doubleValue(x)
    fmt.Println(x)  // Output: 5 — TIDAK berubah!
}

// BENAR: gunakan pointer untuk modifikasi nilai asli
func doubleValuePtr(n *int) {
    *n = *n * 2  // memodifikasi nilai di alamat yang diberikan
}

func main() {
    x := 5
    doubleValuePtr(&x)  // kirim alamat x
    fmt.Println(x)      // Output: 10 — berubah!
}

Pointer ke Struct — Paling Umum #

Pointer paling sering digunakan dengan struct, terutama sebagai return type dan parameter fungsi untuk menghindari copy struct besar:

type User struct {
    ID    int
    Name  string
    Email string
    // bayangkan 20 field lagi...
}

// Return *User lebih efisien dari User untuk struct besar
func NewUser(name, email string) *User {
    return &User{
        Name:  name,
        Email: email,
    }
}

// Pointer receiver — method bisa memodifikasi struct
func (u *User) UpdateEmail(email string) {
    u.Email = email  // Go otomatis dereference: tidak perlu (*u).Email
}

func main() {
    user := NewUser("Budi", "[email protected]")
    user.UpdateEmail("[email protected]")
    fmt.Println(user.Email)  // [email protected]
}

nil Pointer — Sumber Panic yang Umum #

var p *int  // zero value pointer adalah nil

// ANTI-PATTERN: dereference nil pointer → PANIC
fmt.Println(*p)  // panic: runtime error: invalid memory address or nil pointer dereference

// BENAR: selalu cek nil sebelum dereference
if p != nil {
    fmt.Println(*p)
} else {
    fmt.Println("pointer adalah nil")
}

Fungsi new() #

new(T) mengalokasikan memori untuk tipe T, menginisialisasi dengan zero value, dan mengembalikan pointer ke memori tersebut:

p := new(int)          // *int yang menunjuk ke int bernilai 0
fmt.Println(*p)        // 0

s := new(string)       // *string yang menunjuk ke string kosong ""
fmt.Println(*s)        // ""

// Ekuivalen dengan:
n := 0
p2 := &n

Dalam praktiknya, new() jarang digunakan untuk tipe dasar — lebih umum menggunakan &Struct{} untuk struct.


Type Conversion — Selalu Eksplisit #

Go tidak pernah mengkonversi tipe secara implisit. Semua konversi harus ditulis eksplisit. Ini mencegah bug tersembunyi yang umum di C atau JavaScript.

var i int = 42
var f float64 = float64(i)   // int → float64
var u uint = uint(i)          // int → uint
var i32 int32 = int32(i)      // int → int32

// Konversi antara string dan angka
import "strconv"

// String → int
n, err := strconv.Atoi("123")
if err != nil {
    fmt.Println("bukan angka valid")
}

// Int → string
s := strconv.Itoa(456)

// String → float64
f2, err := strconv.ParseFloat("3.14", 64)

// Float → string dengan format
s2 := strconv.FormatFloat(3.14159, 'f', 2, 64)  // "3.14"

Gotcha: Konversi int ke string #

Ini adalah jebakan yang sangat umum:

n := 65
fmt.Println(string(n))       // Output: "A"  ← karakter ASCII 65, BUKAN "65"!
fmt.Println(strconv.Itoa(n)) // Output: "65" ← ini yang biasanya diinginkan
fmt.Println(fmt.Sprintf("%d", n)) // Output: "65"

string(65) mengkonversi integer ke karakter Unicode dengan code point 65 (huruf ‘A’), bukan ke string “65”. Gunakan strconv.Itoa() atau fmt.Sprintf("%d", n) untuk konversi angka ke representasi teksnya.


Type Definition vs Type Alias #

Go mendukung dua cara membuat “nama baru” untuk tipe yang sudah ada, dan keduanya punya semantik yang sangat berbeda.

Type Definition — Tipe Baru #

// Type definition: Celsius adalah tipe BARU yang berbeda dari float64
type Celsius float64
type Fahrenheit float64

var c Celsius = 100
var f Fahrenheit = 212

// Tidak bisa langsung campur meski keduanya berbasis float64
// c = f  // ← compile error: cannot use f (type Fahrenheit) as type Celsius

// Harus konversi eksplisit
c2 := Celsius(f)  // mengkonversi nilai, tapi secara konseptual tidak bermakna

// Type definition memungkinkan method
func (c Celsius) ToFahrenheit() Fahrenheit {
    return Fahrenheit(c*9/5 + 32)
}

func main() {
    boiling := Celsius(100)
    fmt.Println(boiling.ToFahrenheit())  // 212
}

Type Alias — Nama Lain, Tipe yang Sama #

// Type alias: byte adalah nama LAIN untuk uint8, keduanya identik
type byte = uint8   // definisi di standard library
type rune = int32   // definisi di standard library

// Alias bisa saling dipertukarkan tanpa konversi
var b byte = 65
var u uint8 = b  // ✓ tidak perlu konversi — keduanya adalah tipe yang sama persis

Alias digunakan terutama untuk refactoring bertahap (memindahkan tipe dari satu package ke package lain tanpa breaking change) dan untuk nama yang lebih deskriptif seperti byte dan rune.


Contoh Program Lengkap #

Program berikut menggunakan berbagai tipe data dalam konteks sistem inventaris toko:

package main

import (
    "fmt"
    "math"
    "strings"
)

// Type definitions untuk kejelasan semantik
type ProductID int64
type StockUnit uint32
type PriceRupiah int64   // harga dalam satuan rupiah (integer, bukan float!)

type Category byte

const (
    CategoryElectronics Category = iota + 1
    CategoryClothing
    CategoryFood
    CategoryBooks
)

func (c Category) String() string {
    switch c {
    case CategoryElectronics:
        return "Elektronik"
    case CategoryClothing:
        return "Pakaian"
    case CategoryFood:
        return "Makanan"
    case CategoryBooks:
        return "Buku"
    default:
        return fmt.Sprintf("Kategori(%d)", c)
    }
}

type Product struct {
    ID       ProductID
    Name     string
    Category Category
    Price    PriceRupiah
    Stock    StockUnit
    Active   bool
}

// Pointer receiver — memodifikasi stock
func (p *Product) AddStock(qty StockUnit) {
    p.Stock += qty
}

func (p *Product) Sell(qty StockUnit) error {
    if p.Stock < qty {
        return fmt.Errorf("stok tidak cukup: tersedia %d, diminta %d", p.Stock, qty)
    }
    p.Stock -= qty
    return nil
}

// Value receiver — hanya membaca
func (p Product) FormattedPrice() string {
    // Format harga dengan titik sebagai pemisah ribuan
    price := int64(p.Price)
    s := fmt.Sprintf("%d", price)
    // Tambahkan titik setiap 3 digit dari belakang
    var result strings.Builder
    for i, c := range s {
        if i > 0 && (len(s)-i)%3 == 0 {
            result.WriteByte('.')
        }
        result.WriteRune(c)
    }
    return "Rp " + result.String()
}

func (p Product) IsLowStock() bool {
    return p.Stock < 10 && p.Active
}

func calculateDiscount(price PriceRupiah, discountPct float64) PriceRupiah {
    // Kalkulasi diskon tetap akurat karena basis integer
    discount := PriceRupiah(math.Round(float64(price) * discountPct / 100))
    return price - discount
}

func main() {
    products := []Product{
        {
            ID:       1,
            Name:     "Laptop UltraBook Pro",
            Category: CategoryElectronics,
            Price:    15000000,
            Stock:    25,
            Active:   true,
        },
        {
            ID:       2,
            Name:     "Kaos Polos Premium",
            Category: CategoryClothing,
            Price:    85000,
            Stock:    5,
            Active:   true,
        },
        {
            ID:       3,
            Name:     "Go Programming Language",
            Category: CategoryBooks,
            Price:    320000,
            Stock:    50,
            Active:   true,
        },
    }

    fmt.Println("=== INVENTARIS TOKO ===\n")

    for i := range products {
        p := &products[i]  // pointer agar Sell() bisa modifikasi

        fmt.Printf("ID: %d | %s\n", p.ID, p.Name)
        fmt.Printf("  Kategori : %s\n", p.Category)
        fmt.Printf("  Harga    : %s\n", p.FormattedPrice())
        fmt.Printf("  Stok     : %d unit\n", p.Stock)

        if p.IsLowStock() {
            fmt.Printf("  ⚠️  STOK MENIPIS!\n")
        }

        // Simulasi penjualan
        err := p.Sell(3)
        if err != nil {
            fmt.Printf("  Penjualan gagal: %v\n", err)
        } else {
            fmt.Printf("  ✓ Terjual 3 unit, sisa stok: %d\n", p.Stock)
        }

        // Harga setelah diskon 10%
        discounted := calculateDiscount(p.Price, 10)
        fmt.Printf("  Harga diskon 10%%: Rp %d\n", discounted)
        fmt.Println()
    }

    // Demonstrasi konversi tipe
    var totalStock StockUnit
    for _, p := range products {
        totalStock += p.Stock
    }
    fmt.Printf("Total stok semua produk: %d unit\n", totalStock)
    fmt.Printf("Sebagai int: %d\n", int(totalStock))
}

Ringkasan #

  • Gunakan int untuk keperluan umum; int64 untuk ID database dan timestamp; uint8/byte untuk data biner.
  • Integer overflow tidak menyebabkan panic — nilai “memutar” secara silent; validasi rentang jika diperlukan.
  • Selalu pakai float64 kecuali ada alasan spesifik untuk float32 (grafis, interop C).
  • Jangan gunakan float untuk uang — simpan harga dalam integer (rupiah/sen terkecil) untuk menghindari error presisi.
  • len(string) mengembalikan jumlah bytes, bukan karakter; gunakan []rune atau range untuk iterasi per karakter Unicode.
  • byte adalah alias uint8; rune adalah alias int32 untuk karakter Unicode.
  • string(65) menghasilkan "A" (karakter), bukan "65" (teks) — gunakan strconv.Itoa() untuk konversi angka ke teks.
  • Pointer diperlukan ketika fungsi perlu memodifikasi nilai asli atau untuk efisiensi dengan struct besar.
  • Nil pointer dereference menyebabkan panic — selalu cek if p != nil sebelum dereference.
  • Semua konversi tipe selalu eksplisit — Go tidak pernah mengkonversi tipe secara diam-diam.
  • Type definition membuat tipe baru yang tidak kompatibel; type alias membuat nama lain untuk tipe yang sama.

← Sebelumnya: Konstanta   Berikutnya: Operator →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact