1 điểm bởi GN⁺ 2 giờ trước | 1 bình luận | Chia sẻ qua WhatsApp
  • Bản phát hành này tập trung vào generic method, cải thiện struct literal và suy luận kiểu, đồng thời bổ sung các tính năng thực dụng trên toàn bộ thư viện chuẩn, runtime và toolchain
  • Cấp phát bộ nhớ chuyên biệt theo kích thước giúp giảm chi phí của một số lần cấp phát dưới 80 byte tới 30%; với các chương trình nặng về cấp phát, dự kiến hiệu năng tăng khoảng 1% và binary tăng khoảng 60KB
  • encoding/json/v2 được cung cấp chính thức và encoding/json hiện có cũng dùng v2 ở bên trong; bổ sung gói UUID chuẩn và chữ ký ML-DSA dựa trên FIPS 204
  • Hồ sơ goroutineleak, nhãn pprof trong traceback, synctest.Sleep cho thời gian ảo, máy chủ kiểm thử HTTP trong bộ nhớ giúp chẩn đoán và kiểm thử đồng thời dễ hơn
  • Việc kênh timer không còn buffer, tự động đọc hết body phản hồi HTTP, và siết chặt hạn chế //go:linkname có thể ảnh hưởng tới mã hiện có, nên cần kiểm tra tương thích trước khi chuyển sang Go 1.27

Ngôn ngữ và hệ thống kiểu

  • Trọng tâm của Go 1.27 là generic method, cho phép khai báo tham số kiểu riêng độc lập với receiver
    • Trước đây chỉ hàm cấp cao nhất mới có thể là generic, nên cả các phép toán thuộc về một kiểu cũng phải viết dưới dạng hàm của package
    • Có thể định nghĩa dưới dạng method các phép toán trả về kiểu phần tử khác với đầu vào, như Box[T].Map[U]
    • Interface vẫn không thể khai báo method có tham số kiểu, và cũng không thể hiện thực interface bằng generic method
  • Với khóa trong struct literal, ngoài tên field cấp cao nhất, có thể dùng cả selector field hợp lệ
    • Có thể khởi tạo trực tiếp field được promoted từ embedded struct, như User{ID: 7}
  • Suy luận kiểu hàm được mở rộng tới cả conversion và composite literal
    • Suy luận đối số kiểu của hàm generic dựa trên kiểu hàm được kỳ vọng, như []func([]int) int{first, last}
    • Trước đây phải tự instantiate như first[int], last[int]

Runtime và hiệu năng

  • Compiler gọi routine cấp phát chuyên biệt theo kích thước, giảm chi phí một số lần cấp phát bộ nhớ dưới 80 byte tới 30%
    • Mức cải thiện hiệu năng tổng thể trong các chương trình thực tế nặng về cấp phát dự kiến khoảng 1%
    • Không cần sửa mã, nhưng binary tăng khoảng 60KB bất kể workload
    • Có thể tắt bằng GOEXPERIMENT=nosizespecializedmalloc, nhưng tùy chọn này dự kiến bị loại bỏ trong Go 1.28
  • Bổ sung ba tối ưu hóa compiler được bật mặc định
    • known bits data-flow pass theo dõi các bit chắc chắn là 0 hoặc 1 trong giá trị để loại bỏ phép toán dư thừa
    • Di chuyển mã bất biến vòng lặp, đưa các tính toán không đổi trong quá trình lặp ra ngoài vòng lặp
    • Biên dịch switch thỏa điều kiện thành bảng tra cứu, bao gồm cả fallthrough
  • GOEXPERIMENT=mapsplitgroup thử nghiệm đổi bố cục bộ nhớ của nhóm map từ KVKVKVKV sang KKKKVVVV; mặc định đang tắt
  • Linker chuyển type descriptor và itab sang section .go.type chuyên biệt, đồng thời loại bỏ typelinksitablinks
    • reflect.typelinks trả về kiểu thay vì offset, các thư viện truy cập symbol này bằng //go:linkname có thể bị ảnh hưởng

Chẩn đoán goroutine và profiling

  • Các module có go.mod là Go 1.27 trở lên sẽ hiển thị nhãn runtime/pprof trong header goroutine của traceback
    • Ngữ cảnh gắn bằng pprof.Do, như {request: 42}, sẽ xuất hiện trong crash dump, SIGQUIT, và đầu ra runtime.Stack
    • Vẫn có thể dùng GODEBUG=tracebacklabels=0 để tránh lộ nhãn nhạy cảm trong traceback
  • Trình phát hiện rò rỉ goroutine, vốn là tính năng thử nghiệm của Go 1.26, được nâng lên thành profile chính thức goroutineleak
    • Chạy GC để tìm các goroutine vĩnh viễn không thể tiếp tục, chẳng hạn đang kẹt ở channel hoặc mutex, rồi in stack
    • Trong service có thể thu thập qua endpoint /debug/pprof/goroutineleak và không cần GOEXPERIMENT

Mã hóa và định danh

  • Gói crypto/mldsa mới hiện thực chữ ký hậu lượng tử ML-DSA của FIPS 204
    • Ba bộ tham số MLDSA44, MLDSA65, MLDSA87 đánh đổi giữa kích thước khóa/chữ ký và mức bảo mật
    • ML-DSA cũng được bổ sung vào khóa/chữ ký của crypto/x509 và cơ chế chữ ký TLS 1.3 của crypto/tls
  • Gói uuid mới trong thư viện chuẩn tạo và parse UUID RFC 9562 bằng số ngẫu nhiên an toàn về mặt mật mã
    • uuid.New() chọn phương thức phù hợp cho mục đích thông thường; NewV4() tạo UUID thuần ngẫu nhiên, NewV7() tạo UUID theo thứ tự thời gian tạo
    • UUID cấu thành từ số ngẫu nhiên có thể so sánh được, nên có thể dùng trực tiếp ==
    • UUID v7 sắp xếp theo thời gian phù hợp làm khóa cơ sở dữ liệu

JSON và API xử lý dữ liệu

  • encoding/json/v2encoding/json/jsontext cấp thấp được cung cấp mà không cần GOEXPERIMENT=jsonv2
    • encoding/json v1 hiện có cũng dùng implementation v2 ở bên trong
    • Ngoài một số nội dung thông báo lỗi, hành vi hiện có được giữ nguyên nên không cần migration
    • Nếu có vấn đề tương thích, có thể khôi phục implementation v1 gốc bằng GOEXPERIMENT=nojsonv2
    • Vì v2 mặc định không sắp xếp khóa map để tăng hiệu năng, các test cần đầu ra ổn định phải chỉ định json.Deterministic
  • strings.CutLastbytes.CutLast tách giá trị theo dấu phân tách cuối cùng
    • Nếu không có dấu phân tách, trả về toàn bộ input, phần sau rỗng, và false
  • Hasher[T] generic của hash/maphash định nghĩa đồng thời chiến lược hash và so sánh bằng nhau của giá trị
    • Các giá trị bằng nhau bắt buộc phải có cùng hash
    • ComparableHasher[T] cho các kiểu có thể so sánh, và cũng có thể hiện thực chiến lược tùy chỉnh như chuỗi không phân biệt hoa thường
  • math/big.Int.Divide tính đồng thời thương và số dư, đồng thời áp dụng cách làm tròn tường minh trong Trunc, Floor, Round, Ceil
    • Số dư cũng thay đổi theo cách làm tròn, bù đắp hạn chế cắt về 0 của QuoMod hiện có trong mã tài chính/số học
  • math/rand/v2.(*Rand).N tạo số ngẫu nhiên bị giới hạn phạm vi cho số nguyên hoặc kiểu duration từ nguồn *Rand riêng

SIMD di động

  • Gói simd thử nghiệm cung cấp API SIMD độc lập với kích thước vector, bật bằng GOEXPERIMENT=simd
    • Ở môi trường được hỗ trợ, nó biên dịch thành lệnh vector phần cứng thực tế; nếu không, thay bằng mô phỏng Go thuần
    • Số lane của các kiểu như Float32s không cố định mà theo độ rộng phần cứng của từng máy
  • Thư viện chuẩn đã dùng intrinsic simd/archsimd nội bộ
    • MemHash32, MemHash64, StrHash của map Swiss Table được hiện thực lại trên nền SIMD mới

Kiểm thử quyết định luận

  • testing/synctest.Sleep vừa tiến thời gian ảo vừa chờ đến khi mọi goroutine ổn định
    • Trong bong bóng synctest, thời gian thực không trôi qua nên cả chờ 2 giây cũng hoàn tất ngay
    • Thay thế mã lặp lại dùng kết hợp time.Sleepsynctest.Wait hiện có
  • httptest.NewTestServer chạy server bằng mạng ảo trong bộ nhớ thay vì cổng TCP thật
    • srv.Client() kết nối trực tiếp tới handler qua pipe nội bộ trong process
    • Được dọn tự động bằng t.Cleanup, nên không cần defer srv.Close()
    • Khi kết hợp với testing/synctest, cả vòng khứ hồi HTTP cũng có thể được kiểm thử nhanh và quyết định luận trong thời gian ảo

Thay đổi về mạng và tương thích

  • Các channel do time.After, time.NewTimer, time.NewTicker v.v. trả về luôn là channel đồng bộ không buffer
    • Thiết lập GODEBUG asynctimerchan từng khôi phục hành vi có buffer trước đây đã bị loại bỏ
  • Trong HTTP/1, http.Response.Body.Close đọc phần body chưa đọc tới một giới hạn thận trọng để hỗ trợ tái sử dụng kết nối
    • Nếu từng dựa vào Close để dừng sớm một lượt tải lớn, cần tắt tái sử dụng bằng Transport.DisableKeepAlives
  • Server HTTP/2 hiểu priority của client theo RFC 9218 và xử lý các stream ưu tiên cao trước
    • Có thể khôi phục hành vi round-robin cũ bằng Server.DisableClientPriority = true
  • crypto/x509.SystemCertPool trên Windows và macOS hỗ trợ SSL_CERT_FILESSL_CERT_DIR
    • Khi biến được đặt, nó đọc chứng chỉ gốc trên đĩa và dùng trình xác minh của Go thay vì API nền tảng
    • Có thể tắt bằng GODEBUG=x509sslcertoverrideplatform=0
  • Implementation HTTP/2 trước đây nằm trong file generated h2_bundle.go dài 12.226 dòng được thay bằng package net/http/internal/http2 thật
  • net/http được bổ sung hook HTTP/3 có thể cắm được, ở giai đoạn trước khi công khai ra bên ngoài, và một phần bộ test chạy trên HTTP/3
    • Chưa có public API có thể gọi, nhưng đã có nền tảng cho http.Transport dựa trên QUIC trong tương lai

Toolchain

  • go test mặc định chạy kiểm tra vet stdversion để phát hiện việc dùng symbol thư viện chuẩn mới hơn phiên bản Go khai báo trong go.mod
  • Có thể xem tài liệu của một phiên bản module cụ thể bằng go doc pkg@version
  • go doc -ex liệt kê các ví dụ có thể chạy; nếu chỉ định trực tiếp tên ví dụ, nó sẽ in source
  • go fix bổ sung các analyzer hiện đại hóa atomictypes, embedlit, slicesbackward, unsafefuncs
    • waitgroup được đổi tên thành waitgroupgo, còn fmtappendf bị loại bỏ
  • Trong module Go 1.27 trở lên, go mod tidy gộp các khối require rải rác thành hai khối cho dependency trực tiếp/gián tiếp, đồng thời giữ comment
  • go tool trace -http=:6060 chỉ bind vào localhost nếu chỉ cung cấp port; để truy cập từ bên ngoài cần chỉ định địa chỉ
  • Lệnh go loại bỏ hỗ trợ hệ thống quản lý phiên bản Bazaar (bzr)
  • compile, link, asm, cgo, cover, pack hỗ trợ response file (@file) theo kiểu GCC để tránh giới hạn độ dài dòng lệnh

Unicode và ranh giới bảo mật

  • Dữ liệu Unicode của thư viện chuẩn được cập nhật từ Unicode 15 lên Unicode 17
    • Các ký tự được bổ sung trong Unicode 16 cũng được phân loại đúng là ký tự symbol/printable
  • Đã sửa vấn đề có thể thoát ra ngoài root bằng os.Root.ReadDirReaddir
  • Việc dùng //go:linkname không được phê duyệt trở nên khó hơn
    • Chỉ thị linknamestd đánh dấu các linkname chỉ thư viện chuẩn mới có thể import
    • Vì linker cũng kiểm tra truy cập linkname tới symbol assembly, các mã phụ thuộc vào symbol nội bộ không chính thức cần kiểm thử sớm

1 bình luận

 
Ý kiến trên Lobste.rs
  • Có lẽ đây là bản phát hành tốt nhất từ trước đến nay. SIMD, uuid, jsonv2 cuối cùng cũng đã có

    • Có lẽ cũng cần hỗ trợ UUID v5 thì mới có thể chuyển hẳn sang được
  • Hơi lạc đề một chút, nhưng đây là ví dụ điển hình về việc lỗi phát sinh khi coi mọi thứ đều là chuỗi và không xem xét các ràng buộc cũng như mối liên hệ giữa các phương thức của interface
    Equal bỏ qua phân biệt hoa thường, nhưng Hash dùng giá trị đã chuyển thành chữ thường. Tuy nhiên EqualFoldToLower không định nghĩa cùng một quan hệ tương đương
    https://github.com/golang/go/issues/52204
    "\u017F" (ſ, chữ s dài) và "s" là như nhau theo EqualFold, nhưng kết quả sau khi ToLower từng cái lại khác nhau. Vì vậy nếu chèn "s" rồi chèn "ſ" thì hash sẽ khác và bị thêm thành mục riêng
    Điều thú vị hơn là tài liệu ngay tại đó cũng cảnh báo đúng trường hợp này
    https://github.com/golang/go/…

  • Mừng vì có người khác tiếp tục từ chỗ Anton dừng lại

    • Tiếc là có vẻ ít nhất một phần, đặc biệt là đoạn cách dùng JSON v2 cơ bản, được viết bằng LLM. Tôi không thích văn phong đó nhưng nội dung vẫn thú vị
    • Có vẻ Anton không hẳn là đã dừng. Anh ấy cũng viết về 1.26, nên có vẻ bài này chỉ là do người khác đăng trước
  • Tôi thích Go, nhưng cứ tiếp tục thêm các tính năng kiểu này qua mỗi bản phát hành khiến tôi nghi ngờ liệu cú pháp Go có đủ thanh lịch hay không
    func (b Box[T]) Map[U any](f func(T) U) Box[U] {

    • Đây là cảm nhận cá nhân, nhưng tôi không thấy nó tệ hơn các ngôn ngữ khác hỗ trợ tính năng này. Zig là giống nhất, còn Rust·TypeScript·C++20 nhìn chung xem kiểu như một vùng chứa cho định nghĩa phương thức để giảm việc phải lặp lại kiểu receiver trong nhiều phương thức
      Ngược lại, sự dài dòng của Go và Zig lại hay ở chỗ không cần cuộn sang nơi khác để tìm định nghĩa receiver. Ở vị trí gọi hàm — nơi có thể còn quan trọng hơn phần định nghĩa — mọi thứ trông gần như giống nhau và cũng không cần ghi rõ kiểu
  • Tôi đã rời Go một thời gian, nên jsonv2 khá đáng mong đợi. Tuy vậy, nếu nó trở thành mặc định thì có phải Go sẽ phá vỡ cam kết tương thích lâu nay không

    • jsonv2 phải được import từ đường dẫn riêng encoding/json/v2, nên phải tự chọn dùng. encoding/json hiện có dùng v2 ở bên trong nhưng vẫn đảm bảo hành vi cũ, và jsonv2 cũng có cờ để tái hiện các hành vi ngoại lệ của v1, nên không phá vỡ cam kết tương thích
    • Tôi đang dùng thư viện jsontext của v2 trong dự án Go 1.26 và rất hài lòng