Menguasai Struktur Kontrol Program Go yang Unik
Setelah memahami sintaks dasar Go seperti variables dan data types, langkah selanjutnya adalah mempelajari control flow di Go. Struktur kontrol menentukan bagaimana program Anda mengambil keputusan dan menjalankan perulangan.
Control flow Go memiliki keunikan tersendiri dibanding bahasa pemrograman lain. Artikel ini akan membahas secara mendalam:
- If statements dengan fitur unik Go
- Switch statements yang powerful dan fleksibel
- For loops sebagai satu-satunya loop di Go
- Break, continue, dan goto untuk kontrol advanced
- Best practices
Mengapa Control Flow Go Berbeda dan Lebih Baik?
Go control flow dirancang dengan prinsip simplicity dan consistency:
- Tidak ada while atau do-while – hanya
for
loop - Switch tanpa fallthrough default – lebih aman
- If dengan initialization statement – lebih efisien
- Type switch untuk interface handling
- Label dan goto untuk kontrol kompleks
Mari kita eksplorasi setiap komponen ini secara detail.
If Statements di Go: Simple tapi Powerful
1. Basic If Statement
Sintaks dasar if di Go:
if condition {
// code to execute
}
Contoh implementasi:
package main
import "fmt"
func main() {
age := 18
score := 85
// Basic if
if age >= 18 {
fmt.Println("Anda sudah dewasa")
}
// If dengan multiple conditions
if age >= 18 && score >= 80 {
fmt.Println("Memenuhi syarat beasiswa")
}
// If dengan logical operators
if age < 17 || score < 60 {
fmt.Println("Belum memenuhi syarat")
} else {
fmt.Println("Selamat, Anda lolos!")
}
}
2. If-Else dan Else-If Chains
func gradeSystem() {
score := 85
// If-else if-else chain
if score >= 90 {
fmt.Println("Grade: A")
} else if score >= 80 {
fmt.Println("Grade: B")
} else if score >= 70 {
fmt.Println("Grade: C")
} else if score >= 60 {
fmt.Println("Grade: D")
} else {
fmt.Println("Grade: F")
}
// Nested if statements
if score >= 60 {
if score >= 80 {
fmt.Println("Excellent performance!")
} else {
fmt.Println("Good job!")
}
} else {
fmt.Println("Need improvement")
}
}
3. If dengan Initialization Statement (Fitur Unik Go)
Ini adalah fitur yang membuat Go unik:
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
// If dengan initialization - variable hanya available dalam if block
if num := rand.Intn(100); num > 50 {
fmt.Printf("Number %d is greater than 50\n", num)
} else {
fmt.Printf("Number %d is less than or equal to 50\n", num)
}
// 'num' tidak bisa diakses di luar if block
// Practical example: Error handling
if err := someFunction(); err != nil {
fmt.Printf("Error occurred: %v\n", err)
return
}
// File operations example
if data, err := readFile("config.txt"); err != nil {
fmt.Printf("Failed to read file: %v\n", err)
} else {
fmt.Printf("File content: %s\n", data)
}
}
func someFunction() error {
// Simulate function that might return error
if rand.Intn(2) == 0 {
return fmt.Errorf("random error occurred")
}
return nil
}
func readFile(filename string) (string, error) {
// Simulate file reading
return "sample content", nil
}
4. Comparison Operators dalam If
func comparisonExamples() {
a, b := 10, 20
name1, name2 := "Alice", "Bob"
// Numeric comparisons
fmt.Println("a == b:", a == b) // false
fmt.Println("a != b:", a != b) // true
fmt.Println("a < b:", a < b) // true
fmt.Println("a <= b:", a <= b) // true
fmt.Println("a > b:", a > b) // false
fmt.Println("a >= b:", a >= b) // false
// String comparisons
if name1 < name2 { // Lexicographic comparison
fmt.Printf("%s comes before %s\n", name1, name2)
}
// Boolean logic
isActive := true
isVerified := false
if isActive && !isVerified {
fmt.Println("Account is active but not verified")
}
if isActive || isVerified {
fmt.Println("Account has some level of access")
}
}
Switch Statements di Go: Powerful dan Fleksible
1. Basic Switch Statement
Switch di Go tidak memerlukan break statement:
package main
import (
"fmt"
"time"
)
func main() {
day := time.Now().Weekday()
// Basic switch
switch day {
case time.Monday:
fmt.Println("Hari Senin - Semangat kerja!")
case time.Tuesday:
fmt.Println("Hari Selasa - Tetap semangat!")
case time.Wednesday:
fmt.Println("Hari Rabu - Pertengahan minggu!")
case time.Thursday:
fmt.Println("Hari Kamis - Hampir weekend!")
case time.Friday:
fmt.Println("Hari Jumat - TGIF!")
case time.Saturday, time.Sunday:
fmt.Println("Weekend - Waktu istirahat!")
default:
fmt.Println("Hari tidak dikenal")
}
}
2. Switch dengan Multiple Values
func switchMultipleValues() {
grade := 'B'
switch grade {
case 'A', 'a':
fmt.Println("Excellent!")
case 'B', 'b':
fmt.Println("Good job!")
case 'C', 'c':
fmt.Println("Average")
case 'D', 'd':
fmt.Println("Below average")
case 'F', 'f':
fmt.Println("Failed")
default:
fmt.Println("Invalid grade")
}
// Switch dengan range conditions
score := 85
switch {
case score >= 90:
fmt.Println("Grade A")
case score >= 80:
fmt.Println("Grade B")
case score >= 70:
fmt.Println("Grade C")
case score >= 60:
fmt.Println("Grade D")
default:
fmt.Println("Grade F")
}
}
3. Switch dengan Initialization Statement
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
rand.Seed(time.Now().UnixNano())
// Switch dengan initialization
switch num := rand.Intn(100); {
case num < 25:
fmt.Printf("Number %d is in first quartile\n", num)
case num < 50:
fmt.Printf("Number %d is in second quartile\n", num)
case num < 75:
fmt.Printf("Number %d is in third quartile\n", num)
default:
fmt.Printf("Number %d is in fourth quartile\n", num)
}
// Switch untuk error handling
switch err := performOperation(); err {
case nil:
fmt.Println("Operation successful")
case ErrInvalidInput:
fmt.Println("Invalid input provided")
case ErrConnectionFailed:
fmt.Println("Connection failed")
default:
fmt.Printf("Unknown error: %v\n", err)
}
}
// Custom error types
var (
ErrInvalidInput = fmt.Errorf("invalid input")
ErrConnectionFailed = fmt.Errorf("connection failed")
)
func performOperation() error {
// Simulate random operation result
switch rand.Intn(4) {
case 0:
return nil
case 1:
return ErrInvalidInput
case 2:
return ErrConnectionFailed
default:
return fmt.Errorf("unexpected error")
}
}
4. Type Switch (Fitur Advanced)
package main
import "fmt"
func main() {
var items []interface{} = []interface{}{
42,
"hello",
3.14,
true,
[]int{1, 2, 3},
map[string]int{"key": 100},
}
for i, item := range items {
switch v := item.(type) {
case int:
fmt.Printf("Item %d: Integer %d\n", i, v)
case string:
fmt.Printf("Item %d: String '%s' (length: %d)\n", i, v, len(v))
case float64:
fmt.Printf("Item %d: Float %.2f\n", i, v)
case bool:
fmt.Printf("Item %d: Boolean %t\n", i, v)
case []int:
fmt.Printf("Item %d: Integer slice %v\n", i, v)
case map[string]int:
fmt.Printf("Item %d: String-Int map %v\n", i, v)
default:
fmt.Printf("Item %d: Unknown type %T\n", i, v)
}
}
}
5. Fallthrough dalam Switch
func fallthroughExample() {
grade := 'B'
switch grade {
case 'A':
fmt.Println("Excellent!")
fallthrough // Lanjut ke case berikutnya
case 'B':
fmt.Println("Above average")
fallthrough
case 'C':
fmt.Println("Passed the exam")
case 'D':
fmt.Println("Need improvement")
default:
fmt.Println("Failed")
}
// Output untuk grade 'B':
// Above average
// Passed the exam
}
For Loops di Go: Satu Loop untuk Semua Kebutuhan
1. Basic For Loop (C-style)
package main
import "fmt"
func main() {
// Traditional for loop
fmt.Println("Counting up:")
for i := 1; i <= 5; i++ {
fmt.Printf("Count: %d\n", i)
}
// Counting down
fmt.Println("\nCountdown:")
for i := 5; i >= 1; i-- {
fmt.Printf("T-minus %d\n", i)
}
// Custom increment
fmt.Println("\nEven numbers:")
for i := 0; i <= 10; i += 2 {
fmt.Printf("Even: %d\n", i)
}
// Multiple variables
fmt.Println("\nFibonacci sequence:")
for a, b := 0, 1; a < 100; a, b = b, a+b {
fmt.Printf("Fibonacci: %d\n", a)
}
}
2. While-style For Loop
func whileStyleLoop() {
// Go tidak punya while, tapi bisa simulate dengan for
count := 0
for count < 5 {
fmt.Printf("While-style count: %d\n", count)
count++
}
// Infinite loop dengan break condition
counter := 0
for { // Infinite loop
if counter >= 3 {
break
}
fmt.Printf("Infinite loop iteration: %d\n", counter)
counter++
}
}
3. Range-based For Loop
func rangeLoopExamples() {
// Array/Slice iteration
numbers := []int{10, 20, 30, 40, 50}
// With index and value
fmt.Println("Array with index and value:")
for i, num := range numbers {
fmt.Printf("Index %d: %d\n", i, num)
}
// Only value
fmt.Println("\nOnly values:")
for _, num := range numbers {
fmt.Printf("Value: %d\n", num)
}
// Only index
fmt.Println("\nOnly indices:")
for i := range numbers {
fmt.Printf("Index: %d\n", i)
}
// String iteration (runes)
text := "Hello"
fmt.Println("\nString iteration:")
for i, char := range text {
fmt.Printf("Position %d: %c (Unicode: %d)\n", i, char, char)
}
// Map iteration
scores := map[string]int{
"Alice": 95,
"Bob": 87,
"Carol": 92,
}
fmt.Println("\nMap iteration:")
for name, score := range scores {
fmt.Printf("%s: %d\n", name, score)
}
// Channel iteration (akan dibahas di artikel terpisah)
ch := make(chan int, 3)
ch <- 1
ch <- 2
ch <- 3
close(ch)
fmt.Println("\nChannel iteration:")
for value := range ch {
fmt.Printf("Channel value: %d\n", value)
}
}
4. Nested Loops
func nestedLoopExamples() {
// Multiplication table
fmt.Println("Multiplication Table:")
for i := 1; i <= 5; i++ {
for j := 1; j <= 5; j++ {
fmt.Printf("%2d ", i*j)
}
fmt.Println()
}
// Matrix operations
matrix := [][]int{
{1, 2, 3},
{4, 5, 6},
{7, 8, 9},
}
fmt.Println("\nMatrix:")
for i, row := range matrix {
for j, value := range row {
fmt.Printf("matrix[%d][%d] = %d\n", i, j, value)
}
}
// Pattern printing
fmt.Println("\nStar pattern:")
for i := 1; i <= 5; i++ {
for j := 1; j <= i; j++ {
fmt.Print("* ")
}
fmt.Println()
}
}
Break, Continue, dan Goto Statements
1. Break Statement
func breakExamples() {
// Break dalam loop
fmt.Println("Break example:")
for i := 1; i <= 10; i++ {
if i == 6 {
fmt.Println("Breaking at 6")
break
}
fmt.Printf("Number: %d\n", i)
}
// Break dengan label (nested loops)
fmt.Println("\nBreak with label:")
outer:
for i := 1; i <= 3; i++ {
for j := 1; j <= 3; j++ {
if i == 2 && j == 2 {
fmt.Println("Breaking outer loop")
break outer
}
fmt.Printf("i=%d, j=%d\n", i, j)
}
}
// Break dalam switch (otomatis, tapi bisa explicit)
value := 2
switch value {
case 1:
fmt.Println("One")
case 2:
fmt.Println("Two")
// break // Implicit break, tidak perlu ditulis
case 3:
fmt.Println("Three")
}
}
2. Continue Statement
func continueExamples() {
// Skip even numbers
fmt.Println("Odd numbers only:")
for i := 1; i <= 10; i++ {
if i%2 == 0 {
continue // Skip even numbers
}
fmt.Printf("Odd: %d\n", i)
}
// Continue dengan label
fmt.Println("\nContinue with label:")
outer:
for i := 1; i <= 3; i++ {
for j := 1; j <= 3; j++ {
if j == 2 {
fmt.Printf("Skipping i=%d, j=%d\n", i, j)
continue outer
}
fmt.Printf("Processing i=%d, j=%d\n", i, j)
}
}
// Practical example: filtering data
numbers := []int{1, -2, 3, -4, 5, -6, 7, -8, 9, -10}
fmt.Println("\nPositive numbers only:")
for _, num := range numbers {
if num < 0 {
continue
}
fmt.Printf("Positive: %d\n", num)
}
}
3. Goto Statement (Use with Caution)
func gotoExample() {
i := 0
// Simple goto
start:
fmt.Printf("Count: %d\n", i)
i++
if i < 3 {
goto start
}
fmt.Println("Done with goto")
// Goto untuk error handling (lebih baik pakai return)
if err := checkCondition(); err != nil {
goto errorHandler
}
fmt.Println("Normal execution")
goto end
errorHandler:
fmt.Println("Error occurred, cleaning up...")
end:
fmt.Println("Program ends")
}
func checkCondition() error {
// Simulate some condition
return nil
}
Practical Examples: Real-World Control Flow
1. User Input Validation
package main
import (
"fmt"
"strconv"
"strings"
)
func validateUserInput() {
inputs := []string{"25", "abc", "0", "-5", "150"}
for i, input := range inputs {
fmt.Printf("\nValidating input %d: '%s'\n", i+1, input)
// Clean input
cleaned := strings.TrimSpace(input)
if cleaned == "" {
fmt.Println("Empty input")
continue
}
// Convert to integer
if age, err := strconv.Atoi(cleaned); err != nil {
fmt.Printf("Invalid number: %v\n", err)
continue
} else {
// Validate age range
switch {
case age < 0:
fmt.Println("Age cannot be negative")
case age == 0:
fmt.Println("Age cannot be zero")
case age > 120:
fmt.Println("Age seems unrealistic")
case age < 18:
fmt.Printf("Minor: %d years old\n", age)
case age >= 18 && age < 65:
fmt.Printf("Adult: %d years old\n", age)
default:
fmt.Printf("Senior: %d years old\n", age)
}
}
}
}
func main() {
validateUserInput()
}
2. Menu System
func menuSystem() {
for {
fmt.Println("\n=== MAIN MENU ===")
fmt.Println("1. View Profile")
fmt.Println("2. Update Settings")
fmt.Println("3. Check Balance")
fmt.Println("4. Transfer Money")
fmt.Println("5. Exit")
fmt.Print("Choose option (1-5): ")
var choice int
fmt.Scanf("%d", &choice)
switch choice {
case 1:
fmt.Println("Viewing profile...")
viewProfile()
case 2:
fmt.Println("Updating settings...")
updateSettings()
case 3:
fmt.Println("Checking balance...")
checkBalance()
case 4:
fmt.Println("Transfer money...")
if !transferMoney() {
fmt.Println("Transfer cancelled")
}
case 5:
fmt.Println("Goodbye!")
return
default:
fmt.Println("Invalid option. Please choose 1-5")
continue
}
// Wait for user input before showing menu again
fmt.Println("\nPress Enter to continue...")
fmt.Scanln()
}
}
func viewProfile() {
fmt.Println("Name: John Doe")
fmt.Println("Email: john@example.com")
fmt.Println("Member since: 2023")
}
func updateSettings() {
settings := []string{"Language", "Notifications", "Privacy", "Security"}
for i, setting := range settings {
fmt.Printf("%d. %s\n", i+1, setting)
}
}
func checkBalance() {
fmt.Println("Current balance: $1,250.00")
fmt.Println("Available balance: $1,200.00")
}
func transferMoney() bool {
fmt.Print("Enter amount: $")
var amount float64
fmt.Scanf("%f", &amount)
if amount <= 0 {
fmt.Println("Invalid amount")
return false
}
if amount > 1200 {
fmt.Println("Insufficient funds")
return false
}
fmt.Printf("Successfully transferred $%.2f\n", amount)
return true
}
3. Data Processing Pipeline
func dataProcessingPipeline() {
// Simulate raw data
rawData := []map[string]interface{}{
{"name": "Alice", "age": 25, "score": 95, "active": true},
{"name": "", "age": 30, "score": 87, "active": true},
{"name": "Bob", "age": -5, "score": 92, "active": false},
{"name": "Carol", "age": 22, "score": 78, "active": true},
{"name": "Dave", "age": 35, "score": 0, "active": true},
}
fmt.Println("=== DATA PROCESSING PIPELINE ===")
validRecords := 0
totalScore := 0
activeUsers := 0
for i, record := range rawData {
fmt.Printf("\nProcessing record %d: %+v\n", i+1, record)
// Validation pipeline
name, nameOk := record["name"].(string)
age, ageOk := record["age"].(int)
score, scoreOk := record["score"].(int)
active, activeOk := record["active"].(bool)
// Type validation
if !nameOk || !ageOk || !scoreOk || !activeOk {
fmt.Println("Invalid data types")
continue
}
// Business logic validation
switch {
case name == "":
fmt.Println("Name is required")
continue
case age < 0 || age > 100:
fmt.Println("Invalid age range")
continue
case score < 0 || score > 100:
fmt.Println("Invalid score range")
continue
}
// Process valid record
fmt.Println("Record validated")
validRecords++
totalScore += score
if active {
activeUsers++
// Additional processing for active users
switch {
case score >= 90:
fmt.Printf("%s is a top performer!\n", name)
case score >= 80:
fmt.Printf("%s is doing great!\n", name)
default:
fmt.Printf("%s is doing well!\n", name)
}
}
}
// Summary
fmt.Println("\n=== PROCESSING SUMMARY ===")
fmt.Printf("Total records: %d\n", len(rawData))
fmt.Printf("Valid records: %d\n", validRecords)
fmt.Printf("Active users: %d\n", activeUsers)
if validRecords > 0 {
avgScore := float64(totalScore) / float64(validRecords)
fmt.Printf("Average score: %.2f\n", avgScore)
}
}
Best Practices untuk Control Flow
1. Readability dan Maintainability
// Good: Clear and readable
func processOrder(order Order) error {
if order.ID == "" {
return errors.New("order ID is required")
}
if order.Amount <= 0 {
return errors.New("order amount must be positive")
}
switch order.Status {
case "pending":
return processPendingOrder(order)
case "confirmed":
return processConfirmedOrder(order)
default:
return fmt.Errorf("unknown order status: %s", order.Status)
}
}
// Bad: Nested and hard to read
func processOrderBad(order Order) error {
if order.ID != "" {
if order.Amount > 0 {
if order.Status == "pending" {
return processPendingOrder(order)
} else {
if order.Status == "confirmed" {
return processConfirmedOrder(order)
} else {
return fmt.Errorf("unknown order status: %s", order.Status)
}
}
} else {
return errors.New("order amount must be positive")
}
} else {
return errors.New("order ID is required")
}
}
2. Early Return Pattern
// Good: Early return reduces nesting
func validateUser(user User) error {
if user.Email == "" {
return errors.New("email is required")
}
if !isValidEmail(user.Email) {
return errors.New("invalid email format")
}
if user.Age < 13 {
return errors.New("user must be at least 13 years old")
}
// Main logic here
return nil
}
// Bad: Deeply nested conditions
func validateUserBad(user User) error {
if user.Email != "" {
if isValidEmail(user.Email) {
if user.Age >= 13 {
// Main logic here
return nil
} else {
return errors.New("user must be at least 13 years old")
}
} else {
return errors.New("invalid email format")
}
} else {
return errors.New("email is required")
}
}
3. Loop Optimization
// Good: Efficient loop with early termination
func findUser(users []User, targetID string) (User, bool) {
for _, user := range users {
if user.ID == targetID {
return user, true
}
}
return User{}, false
}
// Good: Avoid unnecessary work
func processItems(items []Item) {
if len(items) == 0 {
return // Early return for empty slice
}
for i, item := range items {
if item.IsDeleted {
continue // Skip deleted items
}
if err := processItem(item); err != nil {
fmt.Printf("Failed to process item %d: %v\n", i, err)
continue
}
}
}
Selanjutnya kita akan belajar mengenai Functions di Go: Parameter, Return Values, dan Named Returns – Cara kerja fungsi yang berbeda dari bahasa lain.
Seri Belajar Golang – Pemula
- Pengenalan Golang: Mengapa Google Menciptakan Bahasa Ini?
- Setup Environment dan Hello World di Go
- Sintaks Dasar Go: Variables, Constants, dan Data Types
- Control Flow di Go: If, Switch, dan Loops
- Functions di Go: Parameter, Return Values, dan Named Returns
- Arrays, Slices, dan Maps: Struktur Data Penting di Go
Pingback: Functions di Go: Parameter, Return Values, dan Named Returns - Cara Kerja Fungsi yang Berbeda dari Bahasa Lain
Pingback: Cara Install Golang dan Membuat Program Hello World Pertama
Pingback: Sintaks Dasar Go: Variables, Constants, dan Data Types - Panduan Lengkap
Pingback: Pengenalan Golang: Mengapa Google Menciptakan Bahasa Pemrograman Go?