Giao diện
🏗️ Structs, Methods & Memory Layout
Structs là data container cơ bản nhất trong Go. Nhưng hiểu cách chúng được layout trong memory sẽ giúp bạn viết code hiệu quả hơn đáng kể.
🧠 Struct Internals: Memory Alignment
The Padding Problem
🎓 Professor Tom's Deep Dive: CPU Memory Access
CPU không đọc memory byte-by-byte. Nó đọc theo word size (8 bytes trên 64-bit system).
Alignment Rule: Mỗi field phải được aligned theo bội số của size của nó:
int64(8 bytes) → phải bắt đầu ở địa chỉ chia hết cho 8int32(4 bytes) → phải bắt đầu ở địa chỉ chia hết cho 4bool(1 byte) → có thể bắt đầu ở bất kỳ đâu
Nếu không aligned? CPU phải thực hiện 2 memory reads + bitshift → performance hit.
Bad vs Good Struct Layout
go
// ❌ BAD: 24 bytes (với 7 bytes padding bị waste!)
type BadLayout struct {
Active bool // 1 byte
// ─────────── 7 bytes padding ──────────
ID int64 // 8 bytes (phải aligned 8)
Enabled bool // 1 byte
// ─────────── 7 bytes padding ──────────
}
// ✅ GOOD: 16 bytes (chỉ 6 bytes padding)
type GoodLayout struct {
ID int64 // 8 bytes
Active bool // 1 byte
Enabled bool // 1 byte
// ─────────── 6 bytes padding ──────────
}Memory Visualization
┌─────────────────────────────────────────────────────────────────────┐
│ BadLayout (24 bytes total) │
├─────────────────────────────────────────────────────────────────────┤
│ Offset │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ │
│ │ Act │ PAD │ PAD │ PAD │ PAD │ PAD │ PAD │ PAD │ │
├────────┼─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┤ │
│ Offset │ 8 │ 9 │ 10 │ 11 │ 12 │ 13 │ 14 │ 15 │ │
│ │ ◄───────────── ID (int64) ──────────────────► │ │
├────────┼─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┤ │
│ Offset │ 16 │ 17 │ 18 │ 19 │ 20 │ 21 │ 22 │ 23 │ │
│ │ Enb │ PAD │ PAD │ PAD │ PAD │ PAD │ PAD │ PAD │ │
└────────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴───────────┘
▲ 7 bytes wasted ▲ 7 bytes wasted
┌─────────────────────────────────────────────────────────────────────┐
│ GoodLayout (16 bytes total) │
├─────────────────────────────────────────────────────────────────────┤
│ Offset │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ │
│ │ ◄───────────── ID (int64) ──────────────────► │ │
├────────┼─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┤ │
│ Offset │ 8 │ 9 │ 10 │ 11 │ 12 │ 13 │ 14 │ 15 │ │
│ │ Act │ Enb │ PAD │ PAD │ PAD │ PAD │ PAD │ PAD │ │
└────────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴───────────┘
▲ Only 6 bytes padding (struct alignment)Checking Struct Size
go
import "unsafe"
fmt.Println(unsafe.Sizeof(BadLayout{})) // 24
fmt.Println(unsafe.Sizeof(GoodLayout{})) // 16
// Với 1 triệu records:
// BadLayout: 24MB
// GoodLayout: 16MB → 33% less memory!📌 HPN Standard: Field Ordering
Quy tắc sắp xếp fields:
- Largest first - Xếp fields lớn nhất trước (int64, pointers)
- Same-size group - Nhóm các fields cùng size
- Smallest last - bool, byte xếp cuối cùng
go
// ✅ Production-ready struct
type User struct {
ID int64 // 8 bytes
CreatedAt time.Time // 24 bytes (struct)
Name string // 16 bytes (ptr + len)
Email string // 16 bytes
Age int32 // 4 bytes
IsActive bool // 1 byte
IsAdmin bool // 1 byte
// Total: ≈72 bytes (optimized)
}🔗 Composition over Inheritance
Go KHÔNG có Inheritance
🔥 Raizo's Warning: Mental Model Shift
Nếu bạn từ Java/C++ đến, hãy quên inheritance đi. Go dùng composition.
java
// Java - Inheritance ("is-a")
class Dog extends Animal {
@Override void speak() { ... }
}go
// Go - Composition ("has-a" + interface)
type Dog struct {
animal Animal // Embedded hoặc field
}
func (d Dog) Speak() string { return "Woof!" }Embedded Fields & Field Promotion
go
type Address struct {
Street string
City string
Country string
}
type Person struct {
Name string
Age int
Address // Embedded (không có field name)
}
func main() {
p := Person{
Name: "Raizo",
Age: 30,
Address: Address{
Street: "123 Go Street",
City: "Saigon",
Country: "Vietnam",
},
}
// Field Promotion - truy cập trực tiếp
fmt.Println(p.City) // "Saigon" ← Promoted từ Address
fmt.Println(p.Address.City) // Cũng OK
}Method Promotion
go
type Logger struct{}
func (l Logger) Log(msg string) {
fmt.Println("[LOG]", msg)
}
type Service struct {
Logger // Embedded
Name string
}
func main() {
s := Service{Name: "AuthService"}
s.Log("Started") // ← Method được promoted!
// Tương đương: s.Logger.Log("Started")
}🎓 Under the Hood: Embedding != Inheritance
Quan trọng: Embedded field vẫn là một field riêng biệt trong memory.
go
type Base struct { ID int }
type Derived struct { Base }
d := Derived{Base{ID: 42}}
// Memory layout:
// Derived
// └── Base
// └── ID: 42
// KHÔNG PHẢI:
// Derived extends Base (như Java)Hệ quả:
- Không có
superkeyword - Không có method override (chỉ có shadowing)
- Type assertion
d.(Base)KHÔNG WORK
⚡ Methods & Receivers
Value Receiver vs Pointer Receiver
go
type Counter struct {
Value int
}
// Value Receiver - nhận COPY của struct
func (c Counter) IncrementBad() {
c.Value++ // Chỉ tăng bản copy, original không đổi!
}
// Pointer Receiver - nhận POINTER đến struct
func (c *Counter) IncrementGood() {
c.Value++ // Tăng original
}
func main() {
c := Counter{Value: 0}
c.IncrementBad()
fmt.Println(c.Value) // 0 (không đổi!)
c.IncrementGood()
fmt.Println(c.Value) // 1 (đã tăng)
}Comparison Table
| Aspect | Value Receiver (s S) | Pointer Receiver (s *S) |
|---|---|---|
| Mutation | ❌ Không modify original | ✅ Modify original |
| Copy Cost | Copy toàn bộ struct | Copy 8 bytes (pointer) |
| Nil Safe | ✅ Không panic với nil | ⚠️ Phải check nil |
| Concurrency | ✅ Safe (immutable copy) | ⚠️ Cần mutex |
Memory Visualization
┌─────────────────────────────────────────────────────────────────────┐
│ Value Receiver Call │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ main() stack: IncrementBad() stack: │
│ ┌────────────┐ ┌────────────┐ │
│ │ c.Value: 0 │ ──copy─►│ c.Value: 0 │ ← Bản copy │
│ └────────────┘ └──────┬─────┘ │
│ ↑ │ c.Value++ → c.Value: 1 │
│ │ ▼ │
│ c.Value vẫn = 0! (copy bị destroy khi function return) │
│ │
└─────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ Pointer Receiver Call │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ main() stack: IncrementGood() stack: │
│ ┌────────────┐ ┌─────────────────┐ │
│ │ c.Value: 0 │ ◄────────│ c *Counter │ │
│ └──────┬─────┘ │ (points to c) │ │
│ │ └─────────────────┘ │
│ │ │ │
│ │ c.Value++ thông qua pointer │
│ ▼ │
│ c.Value = 1 ← Modified! │
│ │
└─────────────────────────────────────────────────────────────────────┘HPN Rule: When to Use Which?
📌 HPN Standard: Pointer Receiver by Default
"If in doubt, use Pointer Receiver"
Lý do:
- Consistency - Tất cả methods cùng style
- Performance - Không copy large structs
- Future-proof - Dễ thêm mutation logic sau
Chỉ dùng Value Receiver khi:
- Struct rất nhỏ (< 3 fields, all primitives)
- Method KHÔNG và KHÔNG BAO GIỜ modify state
- Cần immutability guarantee (concurrent safe)
go
// ✅ Good: Consistent pointer receivers
type UserService struct {
repo UserRepository
cache Cache
metrics Metrics
}
func (s *UserService) GetUser(ctx context.Context, id int64) (*User, error) { ... }
func (s *UserService) CreateUser(ctx context.Context, u *User) error { ... }
func (s *UserService) UpdateUser(ctx context.Context, u *User) error { ... }
// ✅ OK: Small immutable struct with value receiver
type Point struct {
X, Y float64
}
func (p Point) Distance(other Point) float64 {
dx := p.X - other.X
dy := p.Y - other.Y
return math.Sqrt(dx*dx + dy*dy)
}🏷️ Struct Tags
Struct tags là metadata gắn vào fields, được đọc bởi reflect package:
go
type User struct {
ID int64 `json:"id" db:"user_id"`
FirstName string `json:"first_name" db:"first_name" validate:"required"`
Email string `json:"email" db:"email" validate:"email"`
Password string `json:"-" db:"password_hash"` // "-" = omit from JSON
CreatedAt time.Time `json:"created_at,omitempty"` // omitempty = skip if zero
}Common Tag Formats
go
// JSON serialization
`json:"field_name"` // Rename field
`json:"field_name,omitempty"` // Skip if zero value
`json:"-"` // Always skip
// Database mapping
`db:"column_name"`
`gorm:"primaryKey;autoIncrement"`
// Validation
`validate:"required,min=3,max=100"`
`validate:"email"`
`validate:"oneof=active inactive pending"`
// Multiple tags
`json:"email" db:"email" validate:"required,email"`Reading Tags with Reflect
go
import "reflect"
func main() {
t := reflect.TypeOf(User{})
field, _ := t.FieldByName("Email")
jsonTag := field.Tag.Get("json") // "email"
dbTag := field.Tag.Get("db") // "email"
validateTag := field.Tag.Get("validate") // "required,email"
}🔥 Raizo's Pitfall: Tag Syntax
Tag phải đúng format, không có space thừa!
go
// ❌ WRONG - có space sau colon
`json: "name"` // Tag sẽ không parse được!
// ✅ CORRECT
`json:"name"`📊 Summary
| Concept | Key Point |
|---|---|
| Memory Alignment | Order fields largest→smallest to minimize padding |
| Composition | Embedded fields ≠ Inheritance, chỉ là field promotion |
| Value Receiver | Copies struct, không modify original |
| Pointer Receiver | References original, use by default |
| Struct Tags | Metadata for JSON, DB, validation |
➡️ Tiếp theo
Structs nắm vững rồi! Tiếp theo: Functions & Methods - Multiple returns, defer, closures, và variadic functions.