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))
}