Giao diện
⚡ Functions & Defer
Go functions return multiple values — đây là signature feature giúp error handling trở nên tự nhiên. Kết hợp với
defer, bạn có một powerful toolkit cho resource management. 📤 Multiple Return Values
Go cho phép return nhiều giá trị từ một function — pattern cốt lõi cho error handling:
go
// Pattern chuẩn: (result, error)
func GetUser(id int64) (*User, error) {
user, err := db.QueryUser(id)
if err != nil {
return nil, fmt.Errorf("get user %d: %w", id, err)
}
return user, nil
}
// Caller
user, err := GetUser(42)
if err != nil {
log.Error("failed to get user", "error", err)
return
}
// Sử dụng user...Multiple Values Use Cases
go
// 1. Value + OK pattern (map lookup, type assertion)
value, ok := myMap["key"]
if !ok {
// key không tồn tại
}
// 2. Quotient + Remainder
func divmod(a, b int) (int, int) {
return a / b, a % b
}
q, r := divmod(17, 5) // q=3, r=2
// 3. Min + Max in one pass
func minMax(nums []int) (min, max int) {
// ... implementation
return
}📌 HPN Standard: Error Handling
Quy tắc vàng: Xử lý error NGAY SAU khi gọi function.
go
// ✅ Correct: Handle immediately
result, err := doSomething()
if err != nil {
return err
}
// Tiếp tục với result...
// ❌ Wrong: Defer error handling
result1, err1 := step1()
result2, err2 := step2()
result3, err3 := step3()
if err1 != nil || err2 != nil || err3 != nil {
// Quá muộn! Bạn đã mất context
}🏷️ Named Return Values
Go cho phép đặt tên cho return values:
go
func parseConfig(path string) (config *Config, err error) {
data, err := os.ReadFile(path)
if err != nil {
return // "naked return" - trả về zero values
}
config = &Config{}
err = json.Unmarshal(data, config)
return // Trả về config và err hiện tại
}Pros vs Cons
| Pros ✅ | Cons ❌ |
|---|---|
| Self-documenting: Tên biến giải thích meaning | Shadowing risk: Dễ shadow bằng := |
| Naked returns: Code ngắn hơn cho simple functions | Readability giảm: Không rõ return cái gì |
| Defer access: Có thể modify trong defer | Implicit behavior: Magic không rõ ràng |
🔥 Raizo's Pitfall: Shadowing Trap
go
func badFunc() (result int, err error) {
if condition {
result, err := compute() // ⚠️ SHADOW! Tạo biến MỚI
if err != nil {
return // Trả về named 'err' (nil), không phải inner err!
}
// result ở đây là inner result, không phải named return
}
return // result = 0, err = nil (không đúng!)
}
// ✅ Fix: Dùng = thay vì :=
func goodFunc() (result int, err error) {
if condition {
result, err = compute() // Assign to named returns
if err != nil {
return
}
}
return
}📌 HPN Standard: Named Returns
- ✅ Dùng cho short functions (<15 lines)
- ✅ Dùng khi cần modify return value trong defer
- ❌ Tránh naked returns trong long functions
- ❌ Không dùng nếu có multiple exit points phức tạp
⏱️ Defer: Resource Management
defer hoãn execution của function call cho đến khi enclosing function return.
go
func processFile(path string) error {
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close() // Chắc chắn sẽ đóng file!
// Process file...
// Dù có return ở đâu, file.Close() vẫn được gọi
return nil
}LIFO (Last-In-First-Out) Principle
🎓 Professor Tom's Deep Dive: Defer Stack
Multiple defers được thực thi theo thứ tự LIFO — như một stack:
go
func main() {
defer fmt.Println("1st defer")
defer fmt.Println("2nd defer")
defer fmt.Println("3rd defer")
fmt.Println("Main body")
}
// Output:
// Main body
// 3rd defer ← Last in, first out
// 2nd defer
// 1st deferVisualization:
┌─────────────────────────────────────┐
│ DEFER STACK │
├─────────────────────────────────────┤
│ TOP → fmt.Println("3rd defer") │ ← Executed first
│ fmt.Println("2nd defer") │
│ fmt.Println("1st defer") │ ← Executed last
└─────────────────────────────────────┘Common Use Cases
go
// 1. File handling
func readConfig() error {
f, err := os.Open("config.json")
if err != nil {
return err
}
defer f.Close()
// ...
}
// 2. Mutex unlock
func (s *Service) UpdateCounter() {
s.mu.Lock()
defer s.mu.Unlock()
s.counter++
}
// 3. Timing/Tracing
func processRequest(req *Request) {
defer trace.StartSpan("processRequest").End()
// ...
}
// 4. Recover from panic
func safeOperation() (err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("panic recovered: %v", r)
}
}()
riskyOperation()
return nil
}Defer với Named Returns (Advanced)
go
func writeFile(path string, data []byte) (err error) {
f, err := os.Create(path)
if err != nil {
return
}
defer func() {
closeErr := f.Close()
if err == nil { // Chỉ update nếu chưa có error
err = closeErr
}
}()
_, err = f.Write(data)
return
}🎓 Under the Hood: Defer Performance
Go 1.14+: Defer overhead gần như eliminated cho simple cases (~0 ns).
Trước Go 1.14: Mỗi defer tốn ~35ns do heap allocation.
go
// Benchmark comparison (pre-1.14 vs post-1.14)
// BenchmarkWithDefer-8 50000000 35.2 ns/op (Go 1.13)
// BenchmarkWithDefer-8 500000000 2.1 ns/op (Go 1.14+)
// Trong hot paths cực kỳ performance-critical,
// vẫn có thể dùng explicit cleanup thay vì deferKết luận: Với Go hiện đại, always use defer cho resource cleanup. Overhead là negligible.
🔄 Closures: Functions Capturing Variables
Closure là function captures variables từ enclosing scope:
go
func counter() func() int {
count := 0
return func() int {
count++ // Capture biến 'count' từ outer function
return count
}
}
func main() {
c := counter()
fmt.Println(c()) // 1
fmt.Println(c()) // 2
fmt.Println(c()) // 3
c2 := counter() // New closure, new 'count'
fmt.Println(c2()) // 1
}Closure Capture Mechanism
🎓 Professor Tom's Deep Dive: How Capture Works
Khi closure capture một variable, nó giữ reference đến variable đó, không phải copy:
go
func createFunctions() []func() {
var funcs []func()
for i := 0; i < 3; i++ {
funcs = append(funcs, func() {
fmt.Println(i) // Capture reference to 'i'
})
}
return funcs
}
func main() {
for _, f := range createFunctions() {
f()
}
}
// Output (Go < 1.22): 3, 3, 3 (tất cả reference cùng 'i')
// Output (Go 1.22+): 0, 1, 2 (mỗi iteration có 'i' riêng)Memory Layout:
┌─────────────────────────────────────────────┐
│ CLOSURE OBJECT │
├─────────────────────────────────────────────┤
│ func ptr │ captured vars (by reference) │
│ ──────── │ ─────────────────────────────│
│ 0x4a2b... │ &count → [actual int value] │
└─────────────────────────────────────────────┘Practical Closure Patterns
go
// 1. Middleware factory
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
next.ServeHTTP(w, r)
log.Printf("%s %s took %v", r.Method, r.URL.Path, time.Since(start))
})
}
// 2. Functional options pattern
type ServerOption func(*Server)
func WithPort(port int) ServerOption {
return func(s *Server) {
s.port = port // Capture 'port'
}
}
// 3. Memoization
func memoize(fn func(int) int) func(int) int {
cache := make(map[int]int)
return func(n int) int {
if v, ok := cache[n]; ok {
return v
}
result := fn(n)
cache[n] = result
return result
}
}📦 Variadic Functions
Variadic functions nhận số lượng arguments không cố định:
go
// Định nghĩa với ...T
func sum(nums ...int) int {
total := 0
for _, n := range nums {
total += n
}
return total
}
// Gọi function
sum(1, 2, 3) // 6
sum(1, 2, 3, 4, 5) // 15
sum() // 0
// Truyền slice với ...
numbers := []int{1, 2, 3, 4}
sum(numbers...) // 10 - "spread" sliceVariadic Rules
go
// 1. Variadic phải là parameter CUỐI CÙNG
func valid(prefix string, nums ...int) {} // ✅
// func invalid(nums ...int, suffix string) {} // ❌ Compile error
// 2. Có thể không truyền gì (empty slice)
func logMessages(msgs ...string) {
if len(msgs) == 0 {
return
}
// ...
}
// 3. Underlying type là slice
func printAll(args ...any) {
fmt.Printf("Type: %T\n", args) // []interface {}
for _, arg := range args {
fmt.Println(arg)
}
}Real-world Examples
go
// fmt.Printf - variadic với any
func Printf(format string, a ...any) (n int, err error)
// append - built-in variadic
slice = append(slice, 1, 2, 3)
slice = append(slice, otherSlice...)
// Custom logger
func (l *Logger) Infof(format string, args ...any) {
l.log(INFO, fmt.Sprintf(format, args...))
}📊 Summary
| Concept | Key Point |
|---|---|
| Multiple Returns | (result, error) pattern cho error handling |
| Named Returns | Self-documenting, nhưng cẩn thận shadowing |
| Defer | LIFO execution, dùng cho resource cleanup |
| Closures | Capture variables by reference |
| Variadic | ...T cho flexible argument count |
➡️ Tiếp theo
Functions nắm vững rồi! Tiếp theo: Slices & Maps Deep Dive - Slice Header, append mechanics, và memory management.