Back to blog

Go Phase 1: Cơ Bản - Cài Đặt, Cú Pháp & Kiểu Dữ Liệu

gogolangprogrammingfundamentalsbackend
Go Phase 1: Cơ Bản - Cài Đặt, Cú Pháp & Kiểu Dữ Liệu

Chào mừng bạn đến với Phase 1 của Go Learning Roadmap! Hướng dẫn toàn diện này bao gồm mọi thứ bạn cần để bắt đầu với Go - từ cài đặt đến viết chương trình hoàn chỉnh đầu tiên.

Bạn Sẽ Học Được Gì

✅ Cài đặt Go và thiết lập môi trường phát triển
✅ Hiểu cấu trúc project và modules của Go
✅ Nắm vững biến, hằng và kiểu dữ liệu cơ bản
✅ Viết luồng điều khiển với if/else, switch và for loops
✅ Tạo hàm với nhiều giá trị trả về
✅ Làm việc với arrays, slices và maps
✅ Sử dụng pointers để làm việc với bộ nhớ hiệu quả


Phần 1: Cài Đặt Go

macOS

# Sử dụng Homebrew (Khuyến nghị)
brew install go
 
# Xác minh cài đặt
go version

Linux

# Tải phiên bản mới nhất
wget https://go.dev/dl/go1.22.0.linux-amd64.tar.gz
 
# Giải nén
sudo rm -rf /usr/local/go
sudo tar -C /usr/local -xzf go1.22.0.linux-amd64.tar.gz
 
# Thêm vào PATH (~/.bashrc hoặc ~/.zshrc)
export PATH=$PATH:/usr/local/go/bin
 
# Xác minh
go version

Windows

  1. Tải installer .msi từ go.dev/dl
  2. Chạy installer
  3. Mở Command Prompt mới
  4. Xác minh với go version

Phần 2: Thiết Lập Môi Trường

VS Code Setup

  1. Cài đặt VS Code
  2. Cài đặt Go Extension (Go Team at Google)
  3. Mở Command Palette → "Go: Install/Update Tools" → Chọn tất cả

Các công cụ quan trọng:

  • gopls - Language server
  • dlv - Debugger
  • gofumpt - Formatter
  • golangci-lint - Linter

Phần 3: Chương Trình Go Đầu Tiên

# Tạo project
mkdir hello-go && cd hello-go
go mod init hello-go

Tạo main.go:

package main
 
import "fmt"
 
func main() {
    fmt.Println("Hello, Go!")
}
# Chạy
go run main.go
 
# Build binary
go build -o hello
./hello

Các Lệnh Go Quan Trọng

LệnhMô tả
go runCompile và chạy
go buildCompile tạo binary
go fmtFormat code
go vetPhát hiện lỗi
go testChạy tests
go mod initKhởi tạo module
go mod tidyDọn dẹp dependencies

Phần 4: Biến và Hằng

Khai Báo Biến

// Cách 1: var với kiểu tường minh
var name string = "Alice"
var age int = 30
 
// Cách 2: var với type inference
var city = "Tokyo"
 
// Cách 3: Short declaration (phổ biến nhất)
country := "Japan"
isActive := true
 
// Cách 4: Nhiều biến
var x, y, z int = 1, 2, 3
a, b := "hello", 42
 
// Cách 5: var block
var (
    firstName = "Bob"
    lastName  = "Smith"
)

Zero Values

Biến chưa khởi tạo nhận zero value:

KiểuZero Value
int, float640
boolfalse
string""
pointers, slices, mapsnil

Hằng Số

const Pi = 3.14159
 
// Hằng block
const (
    StatusOK       = 200
    StatusNotFound = 404
)
 
// iota: tự động tăng
const (
    Sunday = iota // 0
    Monday        // 1
    Tuesday       // 2
)
 
// iota với biểu thức
const (
    _  = iota
    KB = 1 << (10 * iota) // 1024
    MB                    // 1048576
    GB                    // 1073741824
)

Phần 5: Kiểu Dữ Liệu Cơ Bản

Kiểu Số

// Integers
var i int = 42          // platform-dependent (32 hoặc 64 bit)
var i64 int64 = 9223372036854775807
 
// Unsigned
var u8 uint8 = 255      // 0-255
 
// Floating point
var f64 float64 = 3.14159
 
// Complex
var c128 complex128 = 1.5 + 2.5i

Strings và Runes

s := "Hello, 世界"
 
// Độ dài bytes (không phải ký tự!)
len(s)                          // 13 bytes
 
// Độ dài runes (ký tự)
utf8.RuneCountInString(s)       // 9 ký tự
 
// Duyệt theo rune (đúng cách)
for _, r := range s {
    fmt.Printf("%c", r)
}
 
// Rune = int32 cho Unicode
var r rune = '世'

Chuyển Đổi Kiểu

Go yêu cầu chuyển đổi tường minh:

var i int = 42
var f float64 = float64(i)
 
// String ↔ Number
n, _ := strconv.Atoi("123")      // string → int
str := strconv.Itoa(456)          // int → string

Phần 6: Luồng Điều Khiển

If/Else

// If với khởi tạo (idiomatic Go!)
if value := compute(); value > 10 {
    fmt.Println("Value:", value)
}
 
// Pattern xử lý lỗi
if err := doSomething(); err != nil {
    return err
}

Switch

// Switch cơ bản
switch day {
case "Monday":
    fmt.Println("Đầu tuần")
case "Saturday", "Sunday":
    fmt.Println("Cuối tuần!")
default:
    fmt.Println("Giữa tuần")
}
 
// Switch không điều kiện (thay if-else chain)
switch {
case hour < 12:
    fmt.Println("Chào buổi sáng!")
case hour < 17:
    fmt.Println("Chào buổi chiều!")
default:
    fmt.Println("Chào buổi tối!")
}
 
// Type switch
switch t := i.(type) {
case int:
    fmt.Printf("Integer: %d\n", t)
case string:
    fmt.Printf("String: %s\n", t)
}

Lưu ý: Không cần break - các case không "fall through" mặc định.

For Loops

Go chỉ có một cấu trúc lặp: for

// Traditional for
for i := 0; i < 5; i++ {
    fmt.Print(i)
}
 
// While-style
for count < 3 {
    count++
}
 
// Infinite loop
for {
    if condition {
        break
    }
}
 
// Range over slices
for index, value := range fruits {
    fmt.Printf("%d: %s\n", index, value)
}
 
// Range - chỉ value
for _, fruit := range fruits {
    fmt.Println(fruit)
}
 
// Range over maps
for key, value := range ages {
    fmt.Printf("%s: %d\n", key, value)
}

Defer

func cleanup() {
    fmt.Println("Mở file...")
    defer fmt.Println("Đóng file...") // Luôn chạy khi hàm kết thúc
    fmt.Println("Ghi file...")
}

Sử dụng defer cho: đóng files, unlock mutexes, cleanup resources.


Phần 7: Functions

Hàm Cơ Bản

// Hàm đơn giản
func greet(name string) {
    fmt.Println("Hello,", name)
}
 
// Hàm với return
func add(a, b int) int {
    return a + b
}
 
// Nhiều giá trị trả về
func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, fmt.Errorf("chia cho 0")
    }
    return a / b, nil
}
 
// Named return values
func rectangle(w, h float64) (area, perimeter float64) {
    area = w * h
    perimeter = 2 * (w + h)
    return
}

Variadic Functions

func sum(nums ...int) int {
    total := 0
    for _, n := range nums {
        total += n
    }
    return total
}
 
sum(1, 2, 3)           // 6
sum(1, 2, 3, 4, 5)     // 15
 
numbers := []int{10, 20, 30}
sum(numbers...)         // 60

Closures

func makeCounter() func() int {
    count := 0
    return func() int {
        count++
        return count
    }
}
 
counter := makeCounter()
counter() // 1
counter() // 2
counter() // 3

Phần 8: Pointers

x := 42
var p *int = &x        // p chứa địa chỉ của x
 
fmt.Println(*p)        // 42 (dereferencing)
 
*p = 100               // Thay đổi x qua pointer
fmt.Println(x)         // 100

Pointers với Functions

// Pass by value - không thay đổi original
func doubleValue(x int) {
    x = x * 2
}
 
// Pass by pointer - thay đổi original
func doublePointer(x *int) {
    *x = *x * 2
}
 
n := 10
doubleValue(n)   // n vẫn là 10
doublePointer(&n) // n = 20

Khi nào dùng pointers:

  • Cần thay đổi giá trị gốc
  • Truyền structs lớn (tránh copy)
  • Cần biểu thị "không có giá trị" với nil

Xem thêm:


Phần 9: Arrays và Slices

Arrays (Kích thước cố định)

var arr [5]int                    // [0 0 0 0 0]
arr2 := [5]int{1, 2, 3, 4, 5}
arr3 := [...]int{10, 20, 30}      // Compiler đếm
 
// Arrays là VALUE types - copy tạo array mới
arr4 := arr2
arr4[0] = 999
fmt.Println(arr2[0]) // Vẫn là 1!

Slices (Động, Reference Type)

// Tạo với make
s := make([]int, 5)        // length 5, capacity 5
s2 := make([]int, 3, 10)   // length 3, capacity 10
 
// Slice literal
s3 := []int{1, 2, 3, 4, 5}
 
// Slice từ array
arr := [5]int{10, 20, 30, 40, 50}
slice := arr[1:4]          // [20 30 40]
 
// Slices là REFERENCE types
original := []int{1, 2, 3}
reference := original
reference[0] = 999
fmt.Println(original[0])   // 999 (đã thay đổi!)

Slice Operations

// Append
s := []int{1, 2, 3}
s = append(s, 4)
s = append(s, 5, 6, 7)
 
// Append slice khác
more := []int{8, 9}
s = append(s, more...)
 
// Copy
dst := make([]int, len(src))
copy(dst, src)
 
// Delete phần tử tại index i
s = append(s[:i], s[i+1:]...)

Quan trọng: Đọc bài Never Use Arrays in Go


Phần 10: Maps

// Tạo với make
ages := make(map[string]int)
ages["Alice"] = 30
 
// Map literal
scores := map[string]int{
    "Alice": 95,
    "Bob":   87,
}
 
// Kiểm tra key tồn tại
score, exists := scores["David"]
if exists {
    fmt.Println(score)
}
 
// Pattern ngắn gọn
if s, ok := scores["Carol"]; ok {
    fmt.Println(s)
}
 
// Xóa key
delete(scores, "Bob")
 
// Duyệt (thứ tự ngẫu nhiên!)
for name, score := range scores {
    fmt.Printf("%s: %d\n", name, score)
}
 
// nil map - đọc được, KHÔNG ghi được
var nilMap map[string]int
nilMap["key"] = 1 // PANIC!

Map làm Set

set := make(map[string]struct{})
 
for _, item := range items {
    set[item] = struct{}{}
}
 
// Lấy unique items
for item := range set {
    fmt.Println(item)
}

Phần 11: Structs

// Định nghĩa struct
type Person struct {
    FirstName string
    LastName  string
    Age       int
}
 
// Tạo struct
p1 := Person{
    FirstName: "Alice",
    LastName:  "Smith",
    Age:       30,
}
 
// Truy cập fields
fmt.Println(p1.FirstName)
p1.Age = 31
 
// Pointer to struct
p2 := &Person{FirstName: "Bob"}
fmt.Println(p2.FirstName)  // Không cần -> như C!

Struct Embedding (Composition)

type Address struct {
    City    string
    Country string
}
 
type Employee struct {
    Name    string
    Address // Embedded
}
 
emp := Employee{
    Name: "Alice",
    Address: Address{City: "New York"},
}
 
// Truy cập trực tiếp (promoted)
fmt.Println(emp.City)         // New York
fmt.Println(emp.Address.City) // Cũng được

Xem thêm: Composition Over Inheritance


Bài Tập Thực Hành

Bài 1: Temperature Converter

func celsiusToFahrenheit(c float64) float64 {
    return c*9/5 + 32
}
 
func fahrenheitToCelsius(f float64) float64 {
    return (f - 32) * 5 / 9
}

Bài 2: Word Counter

func wordCount(text string) map[string]int {
    words := strings.Fields(strings.ToLower(text))
    counts := make(map[string]int)
    for _, word := range words {
        counts[word]++
    }
    return counts
}

Bài 3: FizzBuzz

func fizzBuzz(n int) string {
    switch {
    case n%15 == 0:
        return "FizzBuzz"
    case n%3 == 0:
        return "Fizz"
    case n%5 == 0:
        return "Buzz"
    default:
        return fmt.Sprintf("%d", n)
    }
}

Lỗi Thường Gặp

1. Shadowing Variables

// SAI: err bị shadow
var err error
if condition {
    result, err := doSomething() // err MỚI!
}
 
// ĐÚNG
var err error
if condition {
    var result int
    result, err = doSomething()
}

2. Loop Variable Capture

// SAI: Tất cả goroutines thấy giá trị cuối
for i := 0; i < 3; i++ {
    go func() {
        fmt.Println(i) // In 3, 3, 3
    }()
}
 
// ĐÚNG: Copy biến
for i := 0; i < 3; i++ {
    i := i
    go func() {
        fmt.Println(i) // In 0, 1, 2
    }()
}

3. Nil Map Write

// SAI
var m map[string]int
m["key"] = 1 // PANIC!
 
// ĐÚNG
m := make(map[string]int)
m["key"] = 1

Tiếp Theo

Chúc mừng bạn đã hoàn thành Phase 1! Bạn đã có nền tảng vững chắc về Go.

Trong Phase 2: Core Go Concepts, bạn sẽ học:

  • Structs và methods chuyên sâu
  • Interfaces và polymorphism
  • Error handling patterns
  • Package organization

Bài viết liên quan:


Tóm Tắt

Trong bài này, bạn đã học:

✅ Cài đặt: Thiết lập Go và môi trường phát triển
✅ Biến: Khai báo, zero values, hằng và iota
✅ Kiểu dữ liệu: Integers, floats, strings, booleans
✅ Luồng điều khiển: if/else, switch, for loops, defer
✅ Functions: Nhiều returns, variadic, closures
✅ Pointers: Địa chỉ bộ nhớ và dereferencing
✅ Arrays & Slices: Mảng cố định vs động
✅ Maps: Hash tables cho key-value storage
✅ Structs: Custom types và embedding

Bạn đã sẵn sàng chuyển sang Phase 2 và khám phá interfaces, error handling và packages!

Happy coding! 🚀

📬 Đăng ký nhận bản tin

Nhận những bài viết mới nhất qua email mỗi tuần. Không spam, hủy đăng ký bất cứ lúc nào.

Chúng tôi tôn trọng quyền riêng tư của bạn. Hủy đăng ký bất cứ lúc nào.

💬 Comments

Đăng nhập để bình luận

Chúng tôi sẽ không đăng bất cứ điều gì mà không có sự cho phép của bạn.