Golang cơ bản- Học trong 7 ngày - Part 1 🚀

EminelEminel
11 min read

🎯 Mục tiêu

Giúp bạn và tôi nắm vững kiến thức cơ bản về ngôn ngữ lập trình Go (Golang) chỉ trong 7 ngày, thông qua lý thuyết trọng tâm, thực hành và mini project.


🗓️ Lộ trình học

NgàyChủ đềMục tiêu học được
1-2Cú pháp cơ bảnHiểu cách khai báo biến, hằng, hàm, if, for, switch
3Struct, Method, PointerBiết dùng struct, method, truyền con trỏ (pointer)
4Slice, Map, InterfaceLàm việc với slice, map và xây dựng interface
5Goroutine, ChannelHiểu lập trình song song với goroutine và channel
6Module, tổ chức projectTổ chức code với module, chia file và package hợp lý
7Mini ProjectXây dựng project nhỏ áp dụng toàn bộ kiến thức đã học

📘 Nội dung chi tiết từng ngày

✅ Ngày 1–2: Cú pháp cơ bản

  • package main, func main()

  • Biến: var, :=, const

  • Hàm: truyền và trả giá trị

  • Câu lệnh điều kiện: if, switch

  • Vòng lặp: for

✅ Ngày 3: Struct, Method, Pointer

  • Tạo struct để biểu diễn đối tượng

  • Gắn method cho struct

  • Truyền con trỏ để cập nhật giá trị

✅ Ngày 4: Slice, Map, Interface

  • Khai báo và thao tác với slice, map

  • Định nghĩa interface và implement nó với struct cụ thể

✅ Ngày 5: Goroutine, Channel

  • Tạo goroutine để chạy song song

  • Giao tiếp giữa các goroutine bằng chan

✅ Ngày 6: Module, tổ chức project

  • Sử dụng go mod

  • Tổ chức project theo module và package

  • Chia code ra nhiều file dễ quản lý

✅ Ngày 7: Mini Project

  • REST API đơn giản với net/http hoặc gin

  • Hoặc CLI tool quản lý việc cần làm

  • Cấu trúc thư mục, xử lý logic, tách controller, model, utils


🛠️ Yêu cầu trước khi bắt đầu

  • Đã biết một ngôn ngữ lập trình khác (C, JavaScript, Python,...)

  • Cài đặt môi trường Go: https://go.dev/doc/install

📚Ngày 1–2: Cú pháp cơ bản trong Golang

1. Package và Hàm Main

Mỗi chương trình Go đều bắt đầu bằng một package, và nếu bạn muốn chương trình chạy được, nó phải có package main và một hàm func main().

package main

import "fmt"

func main() {
    fmt.Println("Hello, Go!")
}
  • package main: Chỉ ra rằng đây là entry point (chương trình chính).

  • func main(): Hàm main là điểm khởi đầu khi chạy chương trình.

2. Khai báo Biến: var, :=, const

  • var: Khai báo có kiểu rõ ràng.
var age int = 30
var name string = "Eminel"
  • :=: Khai báo nhanh, Go tự suy luận kiểu.
age := 30
name := "Eminel"

Dùng := khi bạn đang trong hàm và muốn code gọn hơn. Dùng var/const khi khai báo ở ngoài hàm hoặc muốn rõ kiểu.

  • const: Khai báo hằng số.
const PI = 3.14

3. Hàm – Truyền và Trả Giá Trị

  • Định nghĩa hàm:
func add(a int, b int) int {
    return a + b
}

result := add(3, 5) // result = 8
  • Hàm có thể trả nhiều giá trị:
func swap(a, b string) (string, string) {
    return b, a
}

4. Câu Lệnh Điều Kiện: if, switch

  • if đơn giản:
if age > 18 {
    fmt.Println("Adult")
} else {
    fmt.Println("Teen")
}
  • if có khai báo biến:
if score := 80; score >= 75 {
    fmt.Println("Pass")
}
  • switch:
switch day := "mon"; day {
case "mon":
    fmt.Println("Monday")
case "tue":
    fmt.Println("Tuesday")
default:
    fmt.Println("Unknown")
}

5. Vòng Lặp: for

Go chỉ có một loại vòng lặp duy nhất là for, nhưng nó rất linh hoạt.

  • Dạng C-style:
for i := 0; i < 5; i++ {
    fmt.Println(i)
}
  • Dạng while:
i := 0
for i < 5 {
    fmt.Println(i)
    i++
}
  • Lặp qua slice hoặc map:
nums := []int{1, 2, 3}
for index, value := range nums {
    fmt.Println(index, value)
}

🔚 Tổng Kết

Chủ đềVí dụ
Biếnvar x int = 10, y := "hello"
Hàmfunc add(a int, b int) int
Ifif x &gt; 10 {}
Switchswitch x { case 1: ... }
Vòng lặpfor i := 0; i &lt; 10; i++ {}

📚 Ngày 3: Struct, Method, Pointer trong Go

✅ 1. Struct – Kiểu dữ liệu có cấu trúc

Struct là cách để gom nhiều trường dữ liệu vào một kiểu:

type Person struct {
  name string
  age  int
}

Khởi tạo struct:

p := Person{name: "Eminel", age: 25}

✅ 2. Method – Hàm gắn với struct

Method giúp gắn hành vi với đối tượng:

func (p Person) Greet() {
  fmt.Printf("Xin chào, tôi là %s, %d tuổi.\n", p.name, p.age)
}

(p Person) gọi là receiver – giúp method nhận dữ liệu từ struct Person.

✅ 3. Pointer

Con trỏ trong Golang là gì?

Con trỏ (pointer) là một biến dùng để lưu địa chỉ ô nhớ của một biến khác. Nghe có vẻ khó hiểu? Hãy cùng phân tích từng phần.

Biến là gì?

Khi lập trình, ta cần lưu trữ dữ liệu vào bộ nhớ. Mỗi dữ liệu sẽ nằm ở một địa chỉ ô nhớ cụ thể, ví dụ như 0xAFFFF (địa chỉ dưới dạng hệ thập lục phân).

Để truy cập dữ liệu, ta cần biết địa chỉ ô nhớ nơi dữ liệu được lưu. Nhưng thử tưởng tượng nếu phải nhớ thủ công tất cả địa chỉ đó thì sẽ bất tiện thế nào. Vì vậy, ngôn ngữ lập trình cung cấp biến (variable) – là tên đại diện cho một vùng nhớ.

Vậy con trỏ là gì?

Con trỏ cũng là một biến, nhưng nó đặc biệt ở chỗ giá trị mà nó lưu không phải là số nguyên hay chuỗi thông thường, mà là địa chỉ của một biến khác.

Ví dụ:

var a int = 10
var p *int = &a // p lưu địa chỉ của biến a

Khai báo con trỏ

Cú pháp khai báo một con trỏ trỏ đến kiểu T:

var p *T

Ví dụ, con trỏ trỏ đến kiểu int:

var p *int

Con trỏ này chỉ có thể lưu địa chỉ của các biến kiểu int.

Giá trị mặc định (zero value) của con trỏ là nil. Khi chưa khởi tạo, con trỏ sẽ có giá trị nil.

Ví dụ:

package main
import "fmt"

func main() {
    var p *int
    fmt.Println("p =", p)
}

Kết quả:

p = <nil>

Khởi tạo con trỏ

Bạn có thể gán cho con trỏ địa chỉ của một biến bằng toán tử &:

var x = 100
var p *int = &x

Toán tử & giúp lấy địa chỉ của biến x.

Golang có thể suy luận kiểu của biến con trỏ nên bạn có thể viết gọn:

var p = &x

Ví dụ đầy đủ:

package main
import "fmt"

func main() {
    var a = 5.67
    var p = &a

    fmt.Println("Giá trị của a =", a)
    fmt.Println("Địa chỉ của a =", &a)
    fmt.Println("Giá trị của p =", p)
}

Truy xuất giá trị qua con trỏ (Dereferencing)

Dùng toán tử * để lấy giá trị của biến mà con trỏ trỏ đến:

package main
import "fmt"

func main() {
    var a = 100
    var p = &a

    fmt.Println("a =", a)
    fmt.Println("p =", p)
    fmt.Println("*p =", *p)
}

Toán tử *p cho ta truy cập và chỉnh sửa giá trị của biến gốc:

*p = 2000

Ví dụ:

var a = 1000
var p = &a

fmt.Println("a (trước) =", a)

*p = 2000

fmt.Println("a (sau) =", a)

Tạo con trỏ bằng hàm new()

Golang cung cấp hàm new() để tạo con trỏ:

ptr := new(int)
*ptr = 100

fmt.Printf("ptr = %#x, giá trị = %d\n", ptr, *ptr)

Con trỏ trỏ đến con trỏ

Một con trỏ có thể trỏ đến một biến con trỏ khác:

var a = 7.98
var p = &a
var pp = &p

fmt.Println("a =", a)
fmt.Println("*pp =", *pp)   // => địa chỉ của a
fmt.Println("**pp =", **pp) // => giá trị của a

Golang không hỗ trợ phép toán con trỏ

Khác với C/C++, Golang không hỗ trợ phép toán trên con trỏ như p + 1, p - 1, v.v. Nếu làm vậy sẽ bị lỗi biên dịch.

Tuy nhiên, bạn có thể so sánh hai con trỏ cùng kiểu:

if p1 == p2 {
    fmt.Println("Hai con trỏ trỏ cùng một biến.")
}

Kết luận

  • Con trỏ là biến chứa địa chỉ của biến khác.

  • Có thể khai báo, khởi tạo và truy cập giá trị qua con trỏ.

  • Golang không cho phép toán học trên con trỏ như C/C++.

  • Bạn có thể sử dụng new() để tạo con trỏ.

✅ 4. Ví dụ thực tế: Rectangle

type Rectangle struct {
  width, height float64
}

func (r Rectangle) Area() float64 {
  return r.width * r.height
}

func (r *Rectangle) Resize(newWidth float64) {
  r.width = newWidth
}
  • Area() không thay đổi dữ liệu → dùng Rectangle (value).

  • Resize() thay đổi chiều rộng → dùng *Rectangle (pointer).

🧠 Tổng kết kiến thức

Chủ đềGhi nhớ ngắn
StructGom dữ liệu thành 1 kiểu mới
MethodHàm gắn với struct
Pointer receiverThay đổi dữ liệu thật
Value receiverChỉ đọc, không ảnh hưởng tới dữ liệu gốc

🧠 Ghi nhớ: Nếu bạn muốn method thay đổi dữ liệu gốc, hãy dùng *StructName làm receiver.

📚 Ngày 4: Array, Slice, Map, Interface trong Go

✅ 1. Array – Mảng

Array là một tập hợp các phần tử có cùng kiểu, được lưu trữ liên tục trong bộ nhớ. Kích thước của array là cố định và được xác định khi khai báo.

var arr [3]int // Mảng gồm 3 phần tử kiểu int
arr[0] = 10
  • Kích thước là một phần của kiểu → [3]int khác với [4]int.

  • Khởi tạo mặc định là zero-value của kiểu dữ liệu.

  • Array được copy theo giá trị khi truyền vào hàm → cần lưu ý hiệu suất.

✅ 2. Slice – Mảng động

Slice là mảng có kích thước động, dễ thao tác hơn array.

Tính chất

  • Slice không cần biết trước kích thước.

  • Có thể thêm phần tử với append().

  • Dùng chung dữ liệu với array hoặc slice khác (cùng underlying array).

👉 Hiểu đơn giản: Slice là như một “khay linh hoạt” – có thể mở rộng thêm ô, nhưng bên dưới vẫn dùng "array thật" làm nền.

Khai báo và thao tác:

nums := []int{1, 2, 3}
nums = append(nums, 4)

for i, v := range nums {
  fmt.Println("Index:", i, "Value:", v)
}

Một số hàm với slice:

  • append(slice, item) – thêm phần tử

  • len(slice) – độ dài

  • slice[start:end] – cắt slice

✅ 3. Map – Bản đồ key-value

Map lưu trữ dữ liệu theo cặp key => value.

Tính chất:

  • Dùng để lưu dữ liệu dạng tên → giá trị (key → value).

  • Truy cập nhanh, không quan tâm thứ tự.

  • Có thể kiểm tra xem key có tồn tại không:

👉 Hiểu đơn giản: Map giống như một cái tủ, mỗi ngăn có tên (key), bạn lấy ra giá trị theo tên.

m := map[string]int{"apple": 3, "banana": 5}
fmt.Println(m["apple"]) // in ra 3

Kiểm tra key tồn tại:

val, ok := m["banana"]
if ok {
    fmt.Println("Tồn tại:", val)
}

✅ 4. Interface – Giao diện

Interface định nghĩa hành vi (method) mà struct cần triển khai.

Tính chất:

  • Interface là tập hợp các hành vi (methods).

  • Một struct tự động triển khai interface nếu có đủ method.

  • Giúp viết code tổng quát, linh hoạt, dễ mở rộng.

👉 Hiểu đơn giản: Interface giống như "hợp đồng" – nếu một struct ký hợp đồng đó (có đủ phương thức), thì có thể đưa nó vào hệ thống mà không cần biết bên trong là gì.

type Shape interface {
  Area() float64
}

Struct nào implement đủ các method thì tự động là interface đó:

type Circle struct {
  radius float64
}

func (c Circle) Area() float64 {
  return 3.14 * c.radius * c.radius
}

Dùng interface:

func printArea(s Shape) {
  fmt.Println("Diện tích:", s.Area())
}

🧠 Tổng kết

Kiến thứcMô tả ngắn
SliceMảng động, dùng append, range, len
MapBản đồ key => value, thao tác thêm/xóa
InterfaceĐịnh nghĩa hành vi, cho phép xử lý đa hình

Bài viết này đã dài, tôi xin phép dừng lại tại đây, hẹn gặp các bạn ở phần sau.

0
Subscribe to my newsletter

Read articles from Eminel directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Eminel
Eminel

Hello, my name is Eminel a software engineer specializing in scalable software architecture, microservices, and AI-powered platforms. Passionate about mentoring, performance optimization, and building solutions that drive business success.