- Go 1.21 mở rộng khả năng tương thích dựa trên GODEBUG để bộ công cụ mới tái hiện hành vi của các phiên bản Go cũ một cách ổn định nhất có thể, tập trung vào việc giảm gánh nặng nâng cấp
- Từ Go 1 năm 2012, Go đã cam kết tương thích mã nguồn và giảm hỏng hóc do việc loại bỏ hoặc thay đổi bằng cách kiểm tra API công khai và thử nghiệm nội bộ quy mô lớn
- Ngay cả những cải tiến được tài liệu cho phép như tăng độ chính xác của
time.Now, thay đổi triển khai sort, thay đổi đầu ra của compress/flate, mở rộng đầu vào của strconv.ParseInt, hay thay đổi cách phân tích của net.ParseIP cũng có thể làm hỏng các chương trình hiện có
- Từ Go 1.21, các thiết lập GODEBUG dành cho tương thích sẽ được duy trì tối thiểu 2 năm hoặc 4 bản phát hành Go, và có thể giữ hành vi cũ làm mặc định tùy theo phiên bản
go trong go.mod
- Go 2 sẽ không xuất hiện như một đặc tả mới làm hỏng các chương trình Go 1, và Go ưu tiên tính tương thích để việc nâng cấp bộ công cụ luôn ổn định ngay cả khi bổ sung tính năng mới
Nguyên tắc cơ bản của tương thích Go 1
- Trong Go 1 năm 2012, Go đã đặt ra mục tiêu qua tài liệu “Go 1 and the Future of Go Programs” rằng các chương trình tuân theo đặc tả Go 1 sẽ tiếp tục biên dịch và chạy đúng mà không cần thay đổi trong suốt vòng đời của đặc tả đó
- Trọng tâm của cam kết này là tương thích mã nguồn
- Khi cập nhật lên phiên bản Go mới, mã phải được biên dịch lại
- Có thể bổ sung API mới, nhưng cần tránh những bổ sung làm hỏng mã hiện có
- Không thể đảm bảo rằng mọi thay đổi trong tương lai sẽ tuyệt đối không làm hỏng bất kỳ chương trình nào
- Nếu chương trình phụ thuộc vào hành vi lỗi, nó có thể bị hỏng khi lỗi đó được sửa
- Go cố gắng duy trì việc nâng cấp ổn định trong khi giảm thiểu hỏng hóc nhiều nhất có thể
Ngăn phá vỡ tương thích bằng kiểm tra API công khai
- Trong quá trình phát triển Go, danh sách API công khai của từng package được quản lý trong các tệp riêng biệt với package thực tế
- Ví dụ,
go/api/go1.21.txt ghi lại các mục hàm, phương thức và kiểu của bytes, cmp, context và các package khác
- Các bài kiểm tra tiêu chuẩn xác minh rằng API thực tế của package khớp với các tệp này
- Khi thêm API mới, cũng phải thêm vào tệp API thì kiểm tra mới qua
- Nếu thay đổi hoặc xóa API hiện có, kiểm tra sẽ thất bại
- Không chỉ việc xóa API mà thay đổi kiểu cũng có thể phá vỡ tương thích
os.Stdout là biến toàn cục có kiểu *os.File
- Nếu đổi nó thành một interface có cùng phương thức, mã như
greet(f *os.File) vốn yêu cầu *os.File sẽ bị hỏng
- Kiểm tra API hữu ích trong việc phát hiện thay đổi hoặc xóa API, nhưng không thể ngăn mọi thay đổi không tương thích có thể xảy ra trong Go
Những hỏng hóc tinh vi được phát hiện qua kiểm thử
- Các bản phát triển của bản phát hành Go mới liên tục được kiểm thử trên toàn bộ mã Go nội bộ tại Google
- Nếu kiểm thử thành công, commit đó sẽ được cài vào bộ công cụ Go production của Google
- Nếu kiểm thử nội bộ bị hỏng, điều đó được xem là dấu hiệu mã bên ngoài cũng có thể bị ảnh hưởng và nhóm phát triển sẽ tìm cách giảm tác động
- Trong đa số trường hợp, thay đổi sẽ được hoàn tác hoặc viết lại để không làm hỏng chương trình
- Một số thay đổi dù có thể làm hỏng chương trình vẫn có thể được giữ lại vì quan trọng và được xem là thay đổi tương thích theo tài liệu
- Ngay cả khi đó, phạm vi ảnh hưởng vẫn được giảm thiểu và các vấn đề tiềm ẩn sẽ được ghi trong ghi chú phát hành
Hai ví dụ xuất hiện từ Go 1.1
-
Struct literal và trường mới
- Trong Go 1,
net.TCPAddr là struct có hai trường IP, Port, và composite literal không kèm tên trường vẫn biên dịch được
- Khi Go 1.1 thêm trường
Zone vào net.TCPAddr, mã cũ không còn biên dịch được và báo lỗi “too few initializers in struct literal”
- Cách viết tương thích là dùng literal có gắn nhãn trường
var myAddr = &net.TCPAddr{
IP: net.IPv4(18, 26, 4, 9),
Port: 80,
}
- Nếu không chỉ định
Zone, trường đó sẽ dùng zero value là chuỗi rỗng
- Tài liệu tương thích yêu cầu dùng tagged composite literal với struct của thư viện chuẩn, và
go vet sẽ báo các literal không có nhãn ở những nơi cần để tương thích với các phiên bản sau
-
Độ chính xác thời gian
- Sau Go 1,
time.Now được thay đổi để trả về độ chính xác nano giây thay vì micro giây
- Thay đổi này có thể làm hỏng các bài kiểm tra vốn mong đợi tính đồng nhất sau khi round-trip giá trị
time.Now qua save và load
- Nếu biểu diễn lưu trữ chỉ giữ được độ chính xác micro giây thì trên Go 1 sẽ thành công nhưng trên Go 1.1 có thể thất bại
- Để hỗ trợ sửa các bài kiểm tra như vậy, Go đã thêm các phương thức
Round và Truncate, đồng thời ghi lại vấn đề tiềm ẩn và các phương thức mới trong ghi chú phát hành
- Vì độ chính xác cao hơn là hành vi tốt hơn và nằm trong phạm vi đã được tài liệu hóa của hàm, thay đổi này vẫn được phát hành dù làm hỏng một số chương trình
Ba loại thay đổi có thể phá vỡ tương thích
-
Thay đổi đầu ra
- Thay đổi đầu ra xảy ra khi hàm tạo ra kết quả khác trước đây, nhưng kết quả mới vẫn đúng bằng hoặc đúng hơn kết quả cũ
- Việc thêm độ chính xác nano giây cho
time.Now là ví dụ điển hình
- Trong Go 1.6, triển khai
sort được thay đổi để nhanh hơn khoảng 10%, khiến thứ tự các phần tử được xem là cùng giá trị thay đổi
- Đầu ra Go 1.5:
[red blue green white black yellow orange indigo violet]
- Đầu ra Go 1.6:
[red blue white green black orange yellow indigo violet]
- Việc sắp xếp được phép trả về cùng một kết quả theo bất kỳ thứ tự nào, nhưng các chương trình mong đợi một thứ tự cụ thể sẽ bị hỏng
- Trong Go 1.8,
compress/flate được cải tiến để tạo ra đầu ra nhỏ hơn với chi phí CPU và bộ nhớ tương tự
- Vì vậy, các bản dựng archive có thể tái tạo trong nội bộ Google không còn tái tạo chính xác các archive cũ nữa
- Dự án đó đã fork
compress/flate và compress/gzip để giữ lại thuật toán cũ
- Để chuẩn bị cho thay đổi đầu ra, tốt hơn nên viết chương trình và bài kiểm tra sao cho chấp nhận mọi đầu ra hợp lệ
- Nếu thật sự cần đầu ra tái tạo tuyệt đối, có thể fork mã, nhưng đổi lại sẽ tách khỏi các bản sửa lỗi
-
Thay đổi đầu vào
- Thay đổi đầu vào xảy ra khi hàm thay đổi loại đầu vào mà nó chấp nhận hoặc cách xử lý đầu vào đó
- Go 1.13 thêm cú pháp dấu gạch dưới để tăng tính dễ đọc của số, và
strconv.ParseInt cũng được thay đổi để chấp nhận cú pháp mới này
- Mã của một người dùng bên ngoài, vốn sử dụng số có dấu gạch dưới như một định dạng dữ liệu riêng, đã bị hỏng
- Mã đó ban đầu thử
ParseInt trước và chỉ xử lý dấu gạch dưới khi thất bại, nhưng giờ ParseInt không còn thất bại nữa
net.ParseIP từng chấp nhận địa chỉ IP thập phân có số 0 ở đầu theo các ví dụ RFC IP ban đầu
- Go đọc
18.032.4.011 thành 18.32.4.11
- Các thư viện C kiểu BSD lại diễn giải số 0 ở đầu là bắt đầu bát phân và đọc chuỗi đó thành
18.26.4.9
- Go 1.17 thay đổi
net.ParseIP để từ chối hoàn toàn số 0 ở đầu
- Đây là lựa chọn nhằm đảm bảo rằng khi cả Go và C đều phân tích thành công địa chỉ IP, chúng sẽ có cùng ý nghĩa
- Kubernetes lo ngại các cấu hình đã lưu trước đó có thể không còn phân tích được trên Go 1.17, nên bắt đầu dùng bản fork của
net.ParseIP gốc
- Với đầu vào từ người dùng, tốt hơn là trước tiên xác thực cú pháp được chấp nhận trước khi phân tích giá trị, nhưng trong một số trường hợp có thể vẫn phải fork mã
-
Thay đổi giao thức
- Thay đổi giao thức xảy ra khi thay đổi trong package xuất hiện ra bên ngoài dưới dạng thay đổi ở giao thức giao tiếp với thế giới bên ngoài
- Go 1.6 thêm hỗ trợ HTTP/2 tự động
- Client Go 1.5 chỉ dùng HTTP/1.1 nên có thể hoạt động bình thường trong một số môi trường có thiết bị mạng trung gian đặc thù
- Khi nâng cấp lên Go 1.6, HTTP/2 được sử dụng và chương trình có thể bị hỏng trong những môi trường đó vì HTTP/2 không hoạt động
- Go muốn hỗ trợ mặc định các giao thức hiện đại, nhưng việc bật HTTP/2 vẫn có thể làm hỏng chương trình ngay cả khi không phải lỗi của chương trình hay của chính Go
- Go 1.6 đã ghi lại thay đổi trong ghi chú phát hành và cung cấp cách tắt HTTP/2
- Đặt rõ trường
TLSNextProto
- Đặt
GODEBUG=http2client=0, GODEBUG=http2server=0, hoặc cả hai cùng lúc
- Hỗ trợ chứng chỉ HTTPS dựa trên SHA1 cũng là một ví dụ tinh vi hơn về thay đổi giao thức
- Các tổ chức cấp chứng chỉ đã ngừng phát hành chứng chỉ SHA1 từ năm 2015, và các trình duyệt lớn không còn chấp nhận chúng từ năm 2017
- Go 1.18 tắt hỗ trợ chứng chỉ SHA1 theo mặc định và cho phép vượt qua bằng GODEBUG
- Do một số cài đặt Kubernetes vẫn tiếp tục dùng chứng chỉ SHA1 riêng tư, Go quyết định giữ thiết lập vượt qua này lâu hơn dự kiến
Hỗ trợ GODEBUG mở rộng trong Go 1.21
- Go 1.21 mở rộng và chính thức hóa việc dùng GODEBUG để giảm cả các vấn đề tương thích tinh vi
- Với những thay đổi được quy tắc tương thích Go 1 cho phép nhưng vẫn có thể làm hỏng chương trình hiện có, Go định nghĩa các thiết lập GODEBUG để từng chương trình có thể từ chối hành vi mới
- Có thể có trường hợp không thể thêm thiết lập, nhưng đó được xem là rất hiếm
- Các thiết lập GODEBUG phục vụ tương thích sẽ được duy trì tối thiểu 2 năm, tức 4 bản phát hành Go
- Các thiết lập như
http2client, http2server có thể được giữ lâu hơn nhiều, thậm chí trong một số trường hợp là vô thời hạn
- Khi có thể, mỗi thiết lập GODEBUG sẽ gắn với một bộ đếm
runtime/metrics
- Tên bộ đếm có dạng
/godebug/non-default-behavior/<name>:events
- Ví dụ, nếu đặt
GODEBUG=http2client=0, thì /godebug/non-default-behavior/http2client:events sẽ đếm số HTTP transport được cấu hình không dùng HTTP/2
- Giá trị mặc định của GODEBUG cho chương trình được xác định theo phiên bản Go ghi trong
go.mod của package main
- Nếu
go.mod là go 1.20 và bạn nâng cấp lên bộ công cụ Go 1.21, thì các hành vi do GODEBUG kiểm soát và đã thay đổi trong Go 1.21 sẽ tiếp tục giữ hành vi của Go 1.20 cho đến khi go.mod được đổi thành go 1.21
- Có thể thay đổi từng thiết lập GODEBUG bằng dòng
//go:debug trong package main
- Tất cả thiết lập GODEBUG được tổng hợp trong danh sách trung tâm
Trường hợp panic(nil)
- Trong Go 1.21,
panic(nil) giờ sẽ tạo ra runtime panic không phải nil
- Thay đổi này giúp kết quả
recover có thể cho biết một cách ổn định liệu goroutine hiện tại có đang panic hay không
- Hành vi mới được điều khiển bằng thiết lập GODEBUG và phụ thuộc vào dòng
go trong go.mod của package main
- Nếu là
go 1.20 trở xuống thì panic(nil) vẫn tiếp tục được cho phép
- Nếu là
go 1.21 trở lên thì panic(nil) sẽ chuyển thành panic với runtime.PanicNilError
- Có thể ghi đè rõ ràng giá trị mặc định theo phiên bản bằng cách thêm dòng sau vào
package main
//go:debug panicnil=1
- Cách kết hợp này cho phép cập nhật lên bộ công cụ mới mà vẫn giữ hành vi của bộ công cụ cũ, kiểm soát tinh chỉnh chỉ các thiết lập cần thiết, đồng thời dùng giám sát production để biết có đang sử dụng hành vi không mặc định hay không
- Chi tiết xem tại “Go, Backwards Compatibility, and GODEBUG”
Go 2 không làm hỏng Go 1
- Tài liệu “Go 1 and the Future of Go Programs” từng để ngỏ khả năng một ngày nào đó có thể xuất hiện đặc tả Go 2
- Go 2 theo nghĩa không còn biên dịch được chương trình Go 1 sẽ không xuất hiện
- Go 2 theo nghĩa là một cuộc đại tu lớn của Go 1 khởi đầu từ năm 2017 thì thực chất đã diễn ra rồi
- Go đánh giá tính tương thích có giá trị cao hơn nhiều so với việc cắt đứt với quá khứ, và đã chọn hướng củng cố tương thích hơn nữa
- Trong tương lai, các công việc mới mẻ và thú vị vẫn sẽ tiếp tục, nhưng sẽ được thực hiện theo cách thận trọng và tương thích để việc nâng cấp giữa các bộ công cụ ổn định nhất có thể
1 bình luận
Ý kiến trên Hacker News
Câu hỏi quan trọng về tính tương thích không phải là “có làm hay không”, mà là “làm như thế nào”. Thực ra, thay vì tương thích ngược với quá khứ, điều người ta muốn gần hơn là từ bây giờ mã của mình cứ tiếp tục chạy bình thường
Go 1.21 cung cấp hai điểm cốt lõi khó thấy đồng thời ở các hệ sinh thái ngôn ngữ khác: mỗi thay đổi đều có thiết lập GODEBUG, có thể hoàn tác theo từng thay đổi, và cũng có chỉ số để phát hiện việc có đang dùng cách triển khai cũ hay không. Ngoài ra còn có phiên bản toolchain theo từng module, và có thể tự động lấy toolchain Go cũ hơn hoặc mới hơn một cách an toàn như module
Thêm nữa, nếu chỉ định một phiên bản cụ thể như
go 1.21.2, thì ngay cả khi chạy trên Go mới hơn, các cấu hình opt-out liên quan sẽ được tự động áp dụng cho đến khi bạn yêu cầu rõ ràng hành vi mới. Có thể khai báo ở bất cứ đâu: trong code,go.mod, hoặc biến môi trường, nên đây là một cách đơn giản và đẹp, bao phủ gần như mọi trường hợp sử dụng tương thích từ nhà phát triển đến người triển khaiuse v5.24ở đầu file thì có thể khiến nó hoạt động như Perl 5.24, và áp dụng theo từng file chứ không phải theo moduleChỉ riêng việc có hỗ trợ phiên bản mới hay không đã tạo ra một vấn đề phức tạp đến phi lý, rồi còn hình thành một thung lũng sâu kiểu “chúng tôi nghĩ mình hỗ trợ phiên bản mới, và phiên bản mới cũng nghĩ nó hỗ trợ chúng tôi, nhưng hai bên lại bỏ sót nhau”
Tôi rất thích hướng đi này. Không gì tốt bằng việc bước vào một codebase Go và có thể kỳ vọng rằng chỉ cần nâng phiên bản Go là mọi thứ vẫn chạy tốt
Tuy nhiên, điều đáng lo là hệ thống kiểu khó có thể được cải thiện lớn nếu không có các thay đổi gây vỡ kiểu “đây là code sai nên từ giờ không biên dịch nữa”. Tôi không biết đội Go có quan tâm đến việc này không, nhưng có nhiều “quả thấp dễ hái” có thể tăng đáng kể độ chắc chắn khi biên dịch mà không cần thêm tính năng ngôn ngữ
Ví dụ như báo cáo
nilchưa được kiểm tra, kiểm tra truy cập mảng, suy luận kiểu cho literal struct lồng nhau, kiểm tra đầy đủ enum. Đặc biệt khi viết các lời gọi gRPC lồng nhau, chỉ cần thêm một tầng suy luận kiểu là đủ, vì kiểu đã có sẵn trong chữ ký hàm được gọi, nhưng trong Go việc này quá đau khổNếu hệ thống kiểu của Go được cải thiện, tôi mong những kiểu dữ liệu đại số đơn giản và đẹp như vậy được thêm vào. Chúng thật sự rất hợp với Go, nên mong Go tự tặng mình món quà đó
Hoàn toàn đồng ý. Lý do các bản phát hành Go đáng mong chờ là vì thường chúng thêm những thứ hữu ích mà không làm hỏng gì cả
Không phần nào của ngôn ngữ trông hỏng đến mức không thể sửa bằng thay đổi tương thích ngược. Ngay cả vài cạm bẫy như gán biến vòng lặp nhìn chung cũng đã có đề xuất giữ tương thích
Cá nhân tôi đồng cảm với góc nhìn mong chờ các bản phát hành Go, nhưng sự khác biệt thái độ giữa hai hệ sinh thái thật thú vị
Trước đây tôi từng tổng hợp một số lỗi vỡ đã gặp: https://news.ycombinator.com/item?id=29763324
Với Rust hay gcc, khi nâng compiler bạn nhận được tính năng ngôn ngữ, nhưng phần lớn thư viện phức tạp vẫn giữ nguyên và có thể nâng riêng. Ví dụ HTTP của Rust tách ra thành
hyper, C thì cólibcurlNgược lại, khi nâng compiler Go, bạn không chỉ nhận generics,
embed, tính năng toolchain, mà còn kéo theo thay đổi ởtls, thư viện bảo mật, client/server HTTP. Những khối thư viện chuẩn lớn nhưhttp,tls,cryptolẽ ra nên là thư viện riêng thì tốt hơn nhiều. Như vậy có thể nâng compiler mà không phải sợ, còn thư viện thì nâng theo nhịp của mìnhasync, mà ở edition 2015 vẫn có thể tạo biến hoặc hàm tênasync, nên đó là thay đổi gây vỡTương thích ngược là tốt, nhưng khó chắc rằng một tương lai nơi Go không thể thực hiện đổi mới X nào đó vì phải giữ tương thích là ổn
Tôi rất ủng hộ lập trường rằng Go 2 trong tương lai sẽ tuyệt đối không phá vỡ tính tương thích với Go 1. Nếu phải thay đổi ngôn ngữ đến mức lớn như vậy thì tôi nghĩ tốt hơn là cứ fork rồi đổi tên
Tuy nhiên tôi thắc mắc vì sao không đi xa hơn và nói rằng “sẽ không có Go 2” để loại bỏ sự mơ hồ. Nếu Go 2 trên lý thuyết chạy được mọi chương trình Go 1, thì nó khác gì với một bản phát hành Go 1.xx? Bài viết nói “sẽ không có Go 2 làm hỏng chương trình Go 1”, nhưng có vẻ không nói xa hơn thế
https://github.com/golang/proposal/blob/d661ed19a203000b7c54...
Tài liệu giải thích rằng nếu quá trình trên vận hành đúng kế hoạch, thì theo một nghĩa quan trọng sẽ không có Go 2, mà sẽ chuyển đổi dần dần bằng các tính năng ngôn ngữ và thư viện mới. Đến một thời điểm nào đó, về mặt marketing có thể gọi là “giờ là Go 2”, nhưng cũng có thể bỏ qua luôn. Lập luận là C không có 2.0 thì tại sao Go 2.0 lại cần thiết
Các ngôn ngữ phổ biến như C, C++, Java thực ra cũng gần như luôn là phiên bản 1.N, và tôi nghĩ Go nên làm theo cách đó. Một Go 2 thật sự theo nghĩa một ngôn ngữ mới hoặc thư viện lõi mới không tương thích không phải là lựa chọn tốt cho người dùng, và có thể còn gây hại
Cuối cùng tôi thấy cách đó hợp lý
Nếu có một tính năng ngôn ngữ mới mà cộng đồng rõ ràng mong muốn, và cách đúng hoặc cách duy nhất để đưa tính năng đó vào là một keyword mới, thì vì quy tắc tuyệt đối không được phá vỡ tương thích mã nguồn, tính năng đó sẽ mãi mãi không thể được thêm. Tôi đồng ý với các thay đổi lớn làm đổi ngữ nghĩa của ngôn ngữ, nhưng thay đổi không tương thích ngược không phải lúc nào cũng là thay đổi khổng lồ
Về lý thuyết, ý tưởng rằng tương thích ngược tuyệt đối không thay đổi nghe rất tốt, nhưng trong thực tế cũng có những breaking change có ý nghĩa. Việc phải gánh vĩnh viễn một tính năng ngôn ngữ vì lúc thiết kế ban đầu chưa xét đến X hay Y không hẳn lúc nào cũng là lợi ích
Tôi dùng Go rất nhiều, và hướng đi này thật sự khiến tôi thấy ấm lòng
Tính tương thích có thể không thú vị lắm đối với đội ngũ ngôn ngữ. Vì họ luôn phải đặt chắc một chân ở “quá khứ xa xôi”. Nhưng với người phải duy trì các hệ thống Go lớn, đó thật sự là một món quà lớn
Nếu không thể đưa vào cho đúng cách thì nghĩa là nó chưa đúng, nên chúng tôi thử lại; nếu vẫn không khớp thì có lẽ chúng tôi chưa hiểu vấn đề đủ rõ, vì vậy nên để nó chín muồi lâu hơn
Tôi thật sự thích được làm việc với những người suy nghĩ cẩn trọng và cố gắng để các ý tưởng đúng được đưa vào. Tôi biết ơn việc Russ đóng vai trò BDFL, cũng như được làm việc với Ian, Rob, Rob; nhờ vậy tôi đã trở thành một kỹ sư tốt hơn nhiều
Bài liên quan: Forward Compatibility and Toolchain Management in Go 1.21 - https://news.ycombinator.com/item?id=37122932
Câu “nhàm chán là tốt. Nhàm chán là ổn định. Nhàm chán nghĩa là bạn có thể tập trung vào việc của mình mà không cần bận tâm Go đã thay đổi gì” thật sự rất thấm
Ở công việc chính tôi dùng NodeJS và hệ sinh thái JS nói chung, và đúng là cực khổ. Hệ sinh thái bị phân mảnh, ai cũng làm theo cách riêng nên rất khó tạo ra sự ổn định. Dù vậy tôi vẫn thích công việc này, nhưng sẽ thật tốt nếu hệ sinh thái JS cũng có một nền tảng hiện đại ổn định mà ta có thể tin tưởng và phụ thuộc vào
Rất nhiều công cụ và API đã được viết bằng Go, và việc không cần runtime riêng trên hệ thống đích thường được nêu là một lợi thế lớn. Ngôn ngữ cũng có vẻ đơn giản để học và dùng, hỗ trợ VSC và GoLand đều tốt, còn những phàn nàn phổ biến như xử lý lỗi cũng không có vẻ là khiếm khuyết chí mạng
Tôi tò mò Go còn cần gì nữa để trở thành dòng chính của phát triển trong vài thập kỷ tới, hoặc ít nhất chiếm một phần lớn trên thị trường tuyển dụng. Vì ở một số nơi nó vẫn bị xem là ngôn ngữ ngách
Vấn đề là làm sao đưa tất cả mọi người lên được baseline này. Khi tới đó, tôi nghĩ mọi thứ sẽ tốt hơn nhiều
Ngoài ra hệ sinh thái JS còn bao gồm cả công việc UI front-end, và phạm vi ứng dụng quá rộng nên việc có nhiều triển khai là không thể tránh khỏi. Thậm chí còn là điều nên có
Việc chuyển một phần mã từ Python sang Golang đã giúp ích rất nhiều cho việc mở rộng. Tôi thật sự vui khi thấy Go sẽ tiếp tục giữ lời tuyên bố cốt lõi của mình là tương thích ngược
Nói đến chuyện phân tích cú pháp IP mới nhớ. Tôi luôn tò mò không biết
inet_ntoacủa BSD ban đầu rốt cuộc được viết như thế nàosscanfdùngatoi/atolvà%d/%uthì luôn phân tích đúng theo số nguyên thập phân, nên để tạo ra hiệu ứng kỳ lạ như vậy hẳn phải dùng%ihoặcstrtoutheo cơ số 0inet_atoncủa tôi nói nó đến từ 4.3BSD: https://github.com/dank101/4.3BSD-Reno/blob/master/lib/libc/...inet_addrcủa 4.2BSD còn sớm hơn cũng dùng logic tương tự: https://github.com/dank101/4.2BSD/blob/master/lib/libc/inet/...inet_atonvàinet_addrphân tích địa chỉ theo cách tự nhiên nhất. Nếu dùng những thứ nhưstrtoulhay đặc biệt làsscanfthì hẳn sẽ khá gượng. Vẻ đẹp của con trỏ C nằm ở chỗ nó khiến các tác vụ phân tích cú pháp đơn giản trở nên rất dễ, và có lẽ là quá dễ/etc/hostsKết quả là lại phải quay lại và xóa các số 0 đó
Với tư cách là một người thiết kế ngôn ngữ, tôi tôn trọng các lựa chọn được đưa ra ở đây, bao gồm cả quyết định không tạo ra một Go 2 thật sự
Tôi cũng định áp dụng các kỹ thuật để đảm bảo khả năng tương thích