- Nhóm phát triển TypeScript đã chọn phương án chuyển đổi sang Go thay vì viết lại hoàn toàn để giữ nguyên thông báo lỗi và ngữ nghĩa.
- Cơ chế thu gom rác tự động và closure hạng nhất của Go rất cần thiết để xử lý các cấu trúc dữ liệu phức tạp của trình biên dịch.
- Trình kiểm tra mượn của Rust sẽ buộc phải thực hiện các giải pháp thủ công cho các tham chiếu vòng lặp, làm tăng thêm sự phức tạp không cần thiết.
- Go cung cấp khả năng tạo mã gốc hoàn thiện và xử lý đồng thời bộ nhớ dùng chung mà không cần bất kỳ nỗ lực bổ sung nào.

Khi nhóm TypeScript quyết định chuyển trình biên dịch của họ sang một ngôn ngữ mới, họ có một mục tiêu rõ ràng: giữ cho mọi thứ hoạt động chính xác như trước. Điều đó có nghĩa là bảo toàn các thông báo lỗi và ngữ nghĩa chính xác mà các nhà phát triển đã tin dùng trong nhiều năm. Theo Anders Hejlsberg, kiến trúc sư trưởng, việc viết lại hoàn toàn là không khả thi vì nó sẽ có nguy cơ phá vỡ khả năng tương thích ngược. Thay vào đó, họ chọn phương án chuyển đổi – và quyết định đó đã tạo tiền đề cho một lựa chọn bất ngờ: Sử dụng Rust.
Việc chuyển đổi cần một ngôn ngữ có thể xử lý các cấu trúc nội bộ phức tạp của trình biên dịch mà không cần phải thực hiện những thay đổi lớn. Nhóm nhanh chóng nhận ra rằng việc thu gom rác và các closure hạng nhất là không thể thiếu. Go cung cấp cả hai ngay từ đầu, cùng với khả năng tạo mã gốc hoàn thiện và khả năng xử lý đồng thời bộ nhớ dùng chung trên mọi nền tảng chính. Mặt khác, Rust sẽ yêu cầu các giải pháp thủ công đáng kể, đặc biệt là đối với các cấu trúc dữ liệu vòng lặp của trình biên dịch.
Tại sao lại chọn Rust cho cổng kết nối?
Hejlsberg giải thích rằng trình biên dịch chứa đầy các con trỏ cha, các kiểu đệ quy và các ký hiệu tham chiếu lẫn nhau. Điều này tạo ra các tham chiếu vòng tròn, một đặc điểm tự nhiên trong một ngôn ngữ có cơ chế thu gom rác tự động. Môi trường runtime của Go xử lý vấn đề này một cách liền mạch, cho phép nhóm tập trung vào việc chuyển đổi thay vì phải vật lộn với ngôn ngữ. Trình kiểm tra mượn của Rust, mặc dù mạnh mẽ trong việc đảm bảo an toàn bộ nhớ, nhưng đơn giản là không cho phép hình dạng tham chiếu đó mà không cần sử dụng mã không an toàn hoặc các thủ thuật đếm tham chiếu. Điều đó sẽ làm tăng độ phức tạp và rủi ro, mà không mang lại lợi ích rõ ràng.
Khi so sánh hai ngôn ngữ, nhóm nghiên cứu không tìm thấy lợi thế đáng kể nào về khả năng tạo mã hoặc xử lý song song cho Rust. Khả năng tạo mã gốc của Go đã khá hoàn thiện, và goroutine của nó cung cấp một mô hình đơn giản, hiệu quả cho việc thực thi song song. Hiệu năng của Rust có thể tốt hơn một chút trong một số trường hợp đặc biệt, nhưng nỗ lực bổ sung cần thiết để làm cho trình biên dịch hoạt động với các quy tắc sở hữu của nó là không hợp lý. Việc chuyển đổi cần phải mang tính thực tiễn, chứ không phải là một màn trình diễn các tính năng của ngôn ngữ.
Tính tương thích và ngữ nghĩa: Ưu tiên hàng đầu
Động lực chính đằng sau việc chuyển đổi này là duy trì hành vi giống hệt nhau. Các nhà phát triển phụ thuộc vào thông báo lỗi của TypeScript để gỡ lỗi mã của họ, và bất kỳ thay đổi nào cũng có thể phá vỡ quy trình làm việc của họ. Bằng cách chuyển sang Go, nhóm có thể tái sử dụng logic và cấu trúc dữ liệu hiện có, đảm bảo rằng đầu ra vẫn tương thích từng byte. Cách tiếp cận này cũng giảm nguy cơ phát sinh các lỗi nhỏ mà việc viết lại có thể gây ra.
Cơ chế thu gom rác tự động của Go là một yếu tố then chốt. Đồ thị nội bộ gồm các nút và tham chiếu của trình biên dịch có tính liên kết cao, và việc quản lý bộ nhớ thủ công sẽ là một cơn ác mộng. Với Go, nhóm phát triển có thể tự động cấp phát và giải phóng bộ nhớ, cho phép họ tập trung vào logic của trình biên dịch. Các closure hạng nhất cũng giúp việc triển khai các bước xử lý và biến đổi khác nhau mà trình biên dịch thực hiện trở nên dễ dàng hơn, vì chúng có thể nắm bắt ngữ cảnh một cách tự nhiên.
Công cụ kiểm tra vật phẩm mượn của Rust: Yếu tố quyết định thành bại
Trình kiểm tra mượn của Rust được thiết kế để ngăn chặn các lỗi tranh chấp dữ liệu và lỗi bộ nhớ trong quá trình biên dịch, nhưng nó đi kèm với các quy tắc nghiêm ngặt. Các cấu trúc dữ liệu của trình biên dịch TypeScript chứa đầy các vòng lặp và tham chiếu dùng chung, mà trình kiểm tra mượn sẽ từ chối trừ khi bạn sử dụng các khối không an toàn hoặc Rc/RefCell. Hejlsberg lưu ý rằng điều này sẽ buộc phải thực hiện các giải pháp thủ công cho mọi cấu trúc dữ liệu vòng lặp, làm tăng mã lặp lại và khiến mã khó bảo trì hơn. Không có lợi thế nào về tạo mã hoặc xử lý song song để biện minh cho công việc bổ sung đó.
Cuối cùng, sự lựa chọn đã rõ ràng. Go mang lại sự cân bằng phù hợp giữa tính đơn giản, hiệu suất và khả năng tương thích. Quá trình chuyển đổi hiện đang được tiến hành, và nhóm phát triển tự tin rằng nó sẽ mang lại trải nghiệm TypeScript tương tự với trình biên dịch nhanh hơn và hiệu quả hơn. Đối với các nhà phát triển, điều này có nghĩa là không có gì bất ngờ—chỉ là công cụ đáng tin cậy mà họ vẫn luôn sử dụng, chạy trên nền tảng hiện đại hơn.
Xét về mọi mặt, quyết định chọn Go thay vì Rust cho bản port TypeScript xuất phát từ yếu tố kỹ thuật thực tiễn. Nhu cầu về cơ chế thu gom rác tự động, closure hạng nhất và khả năng xử lý liền mạch các tham chiếu vòng lặp đã khiến Go trở thành lựa chọn phù hợp. Các đảm bảo an toàn của Rust rất ấn tượng, nhưng chúng đi kèm với một cái giá mà nhóm TypeScript không sẵn lòng trả. Kết quả là một bản port giữ nguyên mọi thứ mà các nhà phát triển yêu thích ở TypeScript đồng thời đặt nền móng cho những cải tiến trong tương lai.