Go 1.23 Iterators: Range Over Functions thay đổi code như thế nào

Go 1.23 Iterators: Range Over Functions thay đổi code như thế nào

Go 1.22 đã giới thiệu generics, Go 1.23 tiếp tục đột phá với range over func iterators — khả năng lặp trực tiếp trên hàm thay vì chỉ slice và map. Tính năng này cho phép viết iterator tự định nghĩa, lazy evaluation và pipeline streaming hiệu quả.

Go đã lâu không có cơ chế lặp tùy chỉnh ngoài range over slice, map, channel. Người dùng phải dùng iterator pattern thủ công hoặc thư viện bên thứ ba như samber/lo. Bây giờ ngôn ngữ hỗ trợ native, không cần dependency thêm.

Cú pháp cơ bản

Trước Go 1.23, bạn chỉ có thể dùng range với slice, array, map và channel. Giờ đây, range có thể nhận bất kỳ hàm iterator nào tuân thủ protocol func(func(V) bool).

iter.Seq[int]: func(yield func(int) bool)

func CountTo(n int) iter.Seq[int] {
    return func(yield func(int) bool) {
        for i := 1; i <= n; i++ {
            if !yield(i) {
                return
            }
        }
    }
}

for v := range CountTo(5) {
    fmt.Println(v)
}

Khi yield(i) trả về false, vòng lặp dừng ngay lập tức. Đây là điểm khác biệt so với channel — bạn không cần close channel hay wait goroutine.

iter.Pull và iter.Pull2

Đôi khi bạn cần kiểm soát luồng dữ liệu từ bên trong vòng lặp. iter.Pull trả về hai hàm: next để lấy giá trị tiếp theo và stop để dừng sớm.

next, stop := iter.Pull2(yieldFunc)
defer stop()

v, ok := next()
for ok {
    // xử lý v
    v, ok = next()
}

Cách này đặc biệt hữu ích khi bạn muốn kết hợp vòng lặp với logic phức tạp, như đọc file song song với nhiều goroutine, hoặc xử lý database cursor với transaction quản lý thủ công.

Áp dụng thực tế

Iterator hữu ích cho các tác vụ streaming dữ liệu lớn mà không cần tải toàn bộ vào memory:

  • Đọc và xử lý file CSV hàng triệu dòng
  • Stream dữ liệu từ database với cursor
  • Tạo pipeline xử lý dữ liệu đa bước
  • Tạo sequence vô hạn (Fibonacci, số nguyên tố…)

Ví dụ cụ thể: xử lý file log 50GB, bạn không thể tải toàn bộ vào RAM. Iterator cho phép đọc từng chunk, lọc và ghi kết quả ra file mới mà bộ nhớ gần như không đổi.

So sánh với Channel

Iterator có lợi thế về bộ nhớ vì không cần buffering. Tuy nhiên, channel vẫn phù hợp hơn khi cần đồng bộ hóa giữa nhiều goroutine. Việc lựa chọn phụ thuộc vào pattern concurrent hay pure streaming.

Channel giới hạn bởi buffer size và cơ chế schedule goroutine. Iterator gần như không overhead, vì nó chỉ là function call liên tiếp. Đây là lựa chọn tối ưu cho data pipeline thuần túy.

Khi nào nên dùng

Khi bạn cần xử lý dữ liệu lớn theo từng phần, muốn tạo abstraction tái sử dụng cho vòng lặp, hoặc cần kết hợp nhiều bước xử lý thành pipeline. Đây là giải pháp clean và tiết kiệm bộ nhớ hơn so với việc khởi tạo slice trung gian.

Go tiếp tục tiến hóa về hướng functional programming một cách có chọn lọc. Iterator không phải là breakthrough lớn, nhưng là bước đi đúng đắn giúp code Go trở nên linh hoạt và hiệu quả hơn cho tác vụ data processing. Các bạn có thể tham khảo thêm tại range-functions blogpackage iter chính thức.

So sánh cú pháp range over func so với channel trong Go
Ví dụ pipeline iterator xử lý dữ liệu streaming

Để triển khai iterator hiệu quả, bạn nên đọc kỹ package iter và các ví dụ trong release note. Có nhiều pattern thông minh như iter.Seq2 cho iterator trả về cặp key-value, hay iter.Pull2 với bi-directional iteration.

Bạn cũng có thể kết hợp iterator với slices.Allmaps.All để viết code functional clean hơn. Tính năng này là bước đệm cho Go hướng tới functional programming một cách an toàn và type-safe.

Tôi là một lập trình viên IOS. Code chính là IOS nhưng thỉnnh thoảng vẫn đá sang Android hoặc web. Mặc dù không quá thông thạo nhưng tôi sẽ chia sẻ những kiến thức mà mình đã tìm hiểu, áp dụng qua.

Bài viết liên quan

PostgreSQL JSONB: Lưu trữ và truy vấn dữ liệu không cấu trúc hiệu năng

PostgreSQL JSONB là gì và tại sao mạnh hơn JSON thuần JSONB (Binary JSON) là kiểu dữ liệu PostgreSQL để lưu trữ và truy vấn dữ liệu JSON dưới dạng…

Xem thêm
Zig programming language logo

Zig vs Rust: So sánh ngôn ngữ system programming an toàn bộ nhớ 2025

Zig và Rust là hai ngôn ngữ system programming hiện đại được thiết kế để thay thế C/C++ với an toàn bộ nhớ (memory safety) và hiệu năng cao. Cả…

Xem thêm

Rust ownership borrowing: Hiểu memory safety không cần garbage collector

Rust ownership và borrowing là hai khái niệm cốt lõi tạo nên memory safety đặc trưng của Rust — mà không cần garbage collector, không cần runtime phức tạp. Hiểu…

Xem thêm
0 0 đánh giá
Article Rating
Theo dõi
Thông báo của
guest
0 Comments
Cũ nhất
Mới nhất Được bỏ phiếu nhiều nhất