Struct
Khai báo Struct
Struct là cách nhóm các trường dữ liệu liên quan thành một kiểu mới.
type SinhVien struct {
HoTen string
MSSV string
Tuoi int
DiemTB float64
}
Khởi tạo Struct
// Cách 1: named fields (khuyến khích — tường minh, bền vững)
sv := SinhVien{
HoTen: "Nguyễn Văn A",
MSSV: "SV001",
Tuoi: 20,
DiemTB: 8.5,
}
// Cách 2: positional (không khuyến khích — dễ sai khi thêm field)
sv2 := SinhVien{"Trần Thị B", "SV002", 21, 7.5}
// Cách 3: pointer to struct
sv3 := &SinhVien{HoTen: "Lê C"} // trả về *SinhVien
// Zero-value struct: tất cả field là zero
var sv4 SinhVien // {"", "", 0, 0.0}
Method — Hàm gắn với Struct
// Value receiver: không thay đổi được struct gốc
func (sv SinhVien) XepLoai() string {
switch {
case sv.DiemTB >= 9:
return "Xuất sắc"
case sv.DiemTB >= 8:
return "Giỏi"
case sv.DiemTB >= 6.5:
return "Khá"
default:
return "Trung bình"
}
}
// Pointer receiver: thay đổi được struct gốc
func (sv *SinhVien) CapNhatDiem(diem float64) {
sv.DiemTB = diem
}
Value vs Pointer Receiver
sv := SinhVien{DiemTB: 8.0}
// Value receiver: hoạt động trên bản sao
sv.XepLoai() // OK, không thay đổi sv
// Pointer receiver: thay đổi sv gốc
sv.CapNhatDiem(9.0) // Go tự động lấy &sv
fmt.Println(sv.DiemTB) // 9.0
// Quy tắc: Dùng pointer receiver khi:
// 1. Cần thay đổi struct
// 2. Struct lớn (tránh copy tốn kém)
// 3. Nhất quán: nếu 1 method dùng pointer, nên dùng tất cả
Embedding — Composition thay cho kế thừa
type NguoiDung struct {
Ten string
Email string
}
func (u NguoiDung) Chao() string {
return "Xin chào, " + u.Ten
}
type Admin struct {
NguoiDung // embed: "Admin có NguoiDung"
QuyenHan string
}
a := Admin{
NguoiDung: NguoiDung{Ten: "Admin", Email: "admin@go.dev"},
QuyenHan: "full",
}
fmt.Println(a.Chao()) // "Xin chào, Admin" — dùng method của NguoiDung
fmt.Println(a.Ten) // truy cập field trực tiếp
Struct Tags — Metadata cho Field
type NguoiDung struct {
Ten string `json:"name"`
Email string `json:"email"`
MatKhau string `json:"-"` // dấu - = bỏ qua khi marshal
}
import "encoding/json"
u := NguoiDung{Ten: "An", Email: "an@example.com", MatKhau: "secret"}
data, _ := json.Marshal(u)
// {"name":"An","email":"an@example.com"} — MatKhau không có
example.go
package main
import (
"encoding/json"
"fmt"
"math"
)
type Diem struct {
X, Y float64
}
func (d Diem) KhoangCach(other Diem) float64 {
dx := d.X - other.X
dy := d.Y - other.Y
return math.Sqrt(dx*dx + dy*dy)
}
func (d *Diem) DichChuyen(dx, dy float64) {
d.X += dx
d.Y += dy
}
type HinhTron struct {
TamDiem Diem
BanKinh float64
}
func (h HinhTron) DienTich() float64 {
return math.Pi * h.BanKinh * h.BanKinh
}
func (h HinhTron) ChuVi() float64 {
return 2 * math.Pi * h.BanKinh
}
type Product struct {
ID int `json:"id"`
Name string `json:"name"`
Price float64 `json:"price"`
}
func main() {
p := Diem{X: 0, Y: 0}
q := Diem{X: 3, Y: 4}
fmt.Printf("Khoảng cách P-Q: %.2f\n", p.KhoangCach(q))
p.DichChuyen(1, 1)
fmt.Printf("P sau dịch chuyển: (%.0f, %.0f)\n", p.X, p.Y)
ht := HinhTron{TamDiem: Diem{0, 0}, BanKinh: 5}
fmt.Printf("Diện tích: %.2f\n", ht.DienTich())
fmt.Printf("Chu vi: %.2f\n", ht.ChuVi())
products := []Product{
{1, "Go Book", 199000},
{2, "T-Shirt", 150000},
}
data, _ := json.MarshalIndent(products, "", " ")
fmt.Println(string(data))
}