1. Functions trong Go
Functions là building block cơ bản trong Go. Go functions có nhiều đặc trưng khác biệt so với các ngôn ngữ khác.
1.1. Basic Functions
package main
import "fmt"
// Function cơ bản
func greet(name string) string {
return "Hello, " + name + "!"
}
// Multiple parameters cùng type
func add(a, b int) int {
return a + b
}
// Multiple return values - đặc trưng Go
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, fmt.Errorf("division by zero")
}
return a / b, nil
}
// Named return values
func swap(a, b string) (first, second string) {
first = b
second = a
return // naked return - trả named values
}
func main() {
fmt.Println(greet("Go")) // "Hello, Go!"
fmt.Println(add(3, 5)) // 8
result, err := divide(10, 3)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println(result) // 3.333...
a, b := swap("hello", "world")
fmt.Println(a, b) // "world" "hello"
}
1.2. Variadic Functions
// Variadic function - nhận số lượng tham số bất kỳ
func sum(nums ...int) int {
total := 0
for _, n := range nums {
total += n
}
return total
}
// Mix fixed và variadic params
func printf(format string, args ...interface{}) {
fmt.Printf(format, args...)
}
func main() {
fmt.Println(sum(1, 2, 3)) // 6
fmt.Println(sum(1, 2, 3, 4, 5)) // 15
// Spread slice
nums := []int{10, 20, 30}
fmt.Println(sum(nums...)) // 60
}
1.3. First-class Functions
// Function as variable
var multiply func(int, int) int = func(a, b int) int {
return a * b
}
// Function as parameter (callback)
func apply(a, b int, op func(int, int) int) int {
return op(a, b)
}
// Function as return value (closure)
func makeCounter() func() int {
count := 0
return func() int {
count++
return count
}
}
// Function type alias
type MathFunc func(int, int) int
func calculate(a, b int, fn MathFunc) int {
return fn(a, b)
}
func main() {
// Anonymous function (lambda)
double := func(x int) int {
return x * 2
}
fmt.Println(double(5)) // 10
// Callback
fmt.Println(apply(3, 4, multiply)) // 12
fmt.Println(apply(10, 3, func(a, b int) int {
return a - b
})) // 7
// Closure
counter := makeCounter()
fmt.Println(counter()) // 1
fmt.Println(counter()) // 2
fmt.Println(counter()) // 3
// IIFE (Immediately Invoked Function Expression)
result := func(x int) int {
return x * x
}(5)
fmt.Println(result) // 25
}
1.4. Defer
import "os"
// defer: thực thi khi function return (LIFO order)
func readFile(path string) error {
f, err := os.Open(path)
if err != nil {
return err
}
defer f.Close() // Đảm bảo file luôn được close
// ... đọc file
return nil
}
func main() {
// Defer chạy theo LIFO (Last In, First Out)
defer fmt.Println("1")
defer fmt.Println("2")
defer fmt.Println("3")
// Output: 3, 2, 1
// Use case phổ biến: cleanup resources
// defer mutex.Unlock()
// defer rows.Close()
// defer tx.Rollback()
// defer span.End()
}
2. Interfaces
Interfaces trong Go là implicit — type chỉ cần implement methods, không cần khai báo "implements". Đây là điểm khác biệt lớn so với Java/C#.
2.1. Định nghĩa Interface
// Interface definition
type Shape interface {
Area() float64
Perimeter() float64
}
// Circle implements Shape implicitly
type Circle struct {
Radius float64
}
func (c Circle) Area() float64 {
return math.Pi * c.Radius * c.Radius
}
func (c Circle) Perimeter() float64 {
return 2 * math.Pi * c.Radius
}
// Rectangle implements Shape implicitly
type Rectangle struct {
Width, Height float64
}
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
func (r Rectangle) Perimeter() float64 {
return 2 * (r.Width + r.Height)
}
// Hàm nhận interface
func printShapeInfo(s Shape) {
fmt.Printf("Area: %.2f, Perimeter: %.2f\n", s.Area(), s.Perimeter())
}
func main() {
c := Circle{Radius: 5}
r := Rectangle{Width: 10, Height: 3}
printShapeInfo(c) // Area: 78.54, Perimeter: 31.42
printShapeInfo(r) // Area: 30.00, Perimeter: 26.00
// Slice of interfaces
shapes := []Shape{c, r}
for _, s := range shapes {
printShapeInfo(s)
}
}
2.2. Common Interfaces trong Go
// io.Reader - interface quan trọng nhất
type Reader interface {
Read(p []byte) (n int, err error)
}
// io.Writer
type Writer interface {
Write(p []byte) (n int, err error)
}
// fmt.Stringer (tương đương toString() trong Java)
type Stringer interface {
String() string
}
// error interface
type error interface {
Error() string
}
// sort.Interface
type Interface interface {
Len() int
Less(i, j int) bool
Swap(i, j int)
}
// Implement Stringer
type User struct {
Name string
Age int
}
func (u User) String() string {
return fmt.Sprintf("%s (age %d)", u.Name, u.Age)
}
func main() {
user := User{Name: "Alice", Age: 30}
fmt.Println(user) // "Alice (age 30)" - tự gọi String()
}
2.3. Interface Composition (Embedding)
// Compose interfaces từ các interfaces nhỏ
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int, err error)
}
type Closer interface {
Close() error
}
// Composed interface
type ReadWriter interface {
Reader
Writer
}
type ReadWriteCloser interface {
Reader
Writer
Closer
}
// Best practice: Keep interfaces SMALL
// Go proverb: "The bigger the interface, the weaker the abstraction"
// Thường chỉ 1-3 methods
2.4. Empty Interface & any
// interface{} (Go < 1.18) hoặc any (Go 1.18+) chấp nhận mọi type
func printAnything(v any) {
fmt.Printf("Type: %T, Value: %v\n", v, v)
}
func main() {
printAnything(42) // Type: int, Value: 42
printAnything("hello") // Type: string, Value: hello
printAnything(true) // Type: bool, Value: true
printAnything([]int{1, 2}) // Type: []int, Value: [1 2]
// map[string]any - thường dùng cho JSON
data := map[string]any{
"name": "Alice",
"age": 30,
"roles": []string{"admin", "user"},
}
fmt.Println(data)
}
3. Error Handling
Go không có exceptions (try-catch). Error handling dùng explicit return values — đây là design choice có chủ đích.
3.1. Basic Error Handling
import (
"errors"
"fmt"
"os"
"strconv"
)
// Hàm trả về (result, error)
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("division by zero")
}
return a / b, nil
}
// fmt.Errorf với format string
func findUser(id int) (*User, error) {
if id <= 0 {
return nil, fmt.Errorf("invalid user ID: %d", id)
}
// ... lookup user
return &User{Name: "Alice"}, nil
}
func main() {
// Pattern: check error immediately
result, err := divide(10, 0)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println(result)
// Chaining error checks
f, err := os.Open("config.json")
if err != nil {
fmt.Println("Cannot open file:", err)
return
}
defer f.Close()
// strconv error example
num, err := strconv.Atoi("abc")
if err != nil {
fmt.Println("Parse error:", err)
}
fmt.Println(num)
}
3.2. Custom Errors
// Custom error type
type ValidationError struct {
Field string
Message string
}
func (e *ValidationError) Error() string {
return fmt.Sprintf("validation failed on %s: %s", e.Field, e.Message)
}
// Sentinel errors (pre-defined errors)
var (
ErrNotFound = errors.New("not found")
ErrUnauthorized = errors.New("unauthorized")
ErrForbidden = errors.New("forbidden")
)
func getUser(id int) (*User, error) {
if id <= 0 {
return nil, &ValidationError{
Field: "id",
Message: "must be positive",
}
}
// ... user not in DB
return nil, fmt.Errorf("user %d: %w", id, ErrNotFound)
}
3.3. Error Wrapping (Go 1.13+)
import "errors"
func readConfig() error {
f, err := os.Open("config.yaml")
if err != nil {
// %w wraps the original error
return fmt.Errorf("readConfig: %w", err)
}
defer f.Close()
// ... parse
return nil
}
func initApp() error {
if err := readConfig(); err != nil {
return fmt.Errorf("initApp: %w", err)
}
return nil
}
func main() {
err := initApp()
if err != nil {
fmt.Println(err)
// "initApp: readConfig: open config.yaml: no such file or directory"
// errors.Is - check nếu error chain chứa target error
if errors.Is(err, os.ErrNotExist) {
fmt.Println("File does not exist")
}
// errors.As - extract specific error type từ chain
var pathErr *os.PathError
if errors.As(err, &pathErr) {
fmt.Println("Path:", pathErr.Path)
fmt.Println("Op:", pathErr.Op)
}
}
}
3.4. Panic & Recover
// panic: dùng cho unrecoverable errors (thay vì throw)
// recover: bắt panic (thay vì catch)
func riskyOperation() {
// panic dừng execution, unwind stack
panic("something went terribly wrong")
}
// recover chỉ hoạt động trong defer
func safeOperation() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered from panic:", r)
}
}()
riskyOperation()
fmt.Println("This won't print")
}
func main() {
safeOperation()
fmt.Println("Program continues normally")
}
// ⚠️ Quy tắc:
// - KHÔNG dùng panic cho business logic errors
// - Dùng panic khi: programmer error, unrecoverable state
// - recover thường dùng trong middleware (HTTP handler recovery)
3.5. Error Handling Patterns thường dùng
// Pattern 1: Early return
func processOrder(order *Order) error {
if order == nil {
return errors.New("order is nil")
}
if order.Total <= 0 {
return fmt.Errorf("invalid total: %.2f", order.Total)
}
if err := validateItems(order.Items); err != nil {
return fmt.Errorf("validate items: %w", err)
}
if err := chargePayment(order); err != nil {
return fmt.Errorf("charge payment: %w", err)
}
return nil
}
// Pattern 2: Error type assertion for flow control
func handleError(err error) {
var validErr *ValidationError
if errors.As(err, &validErr) {
// 400 Bad Request
fmt.Printf("Validation: %s - %s\n", validErr.Field, validErr.Message)
return
}
if errors.Is(err, ErrNotFound) {
// 404 Not Found
fmt.Println("Resource not found")
return
}
if errors.Is(err, ErrUnauthorized) {
// 401 Unauthorized
fmt.Println("Please login")
return
}
// 500 Internal Server Error
fmt.Println("Internal error:", err)
}
// Pattern 3: errors.Join (Go 1.20+) - multiple errors
func validateUser(u *User) error {
var errs []error
if u.Name == "" {
errs = append(errs, errors.New("name is required"))
}
if u.Email == "" {
errs = append(errs, errors.New("email is required"))
}
if u.Age < 0 {
errs = append(errs, errors.New("age must be non-negative"))
}
return errors.Join(errs...)
}
4. Struct Embedding (Composition)
// Go dùng composition thay vì inheritance
type BaseModel struct {
ID int
CreatedAt time.Time
UpdatedAt time.Time
}
type User struct {
BaseModel // Embedding - "kế thừa" tất cả fields và methods
Name string
Email string
}
type Post struct {
BaseModel
Title string
Content string
Author User
}
func main() {
user := User{
BaseModel: BaseModel{
ID: 1,
CreatedAt: time.Now(),
},
Name: "Alice",
Email: "[email protected]",
}
// Access embedded fields trực tiếp
fmt.Println(user.ID) // 1 (từ BaseModel)
fmt.Println(user.Name) // "Alice"
fmt.Println(user.CreatedAt) // (từ BaseModel)
}
5. Tổng kết
- Functions: Multiple return values, first-class, closures, defer
- Interfaces: Implicit implementation, small interfaces, composition
- Error handling: Explicit errors, wrapping (%w), errors.Is/As, custom errors
- Composition: Struct embedding thay vì inheritance
- Panic/Recover: Chỉ cho unrecoverable errors, không dùng cho business logic
Bài tiếp theo: Generics, Packages & Modules — hệ thống type parameters và quản lý code trong Go.