- Scylla đề xuất một lộ trình cho phép biên dịch trực tiếp sang Rust an toàn bằng cách dần cấu trúc lại mã C gốc, thay vì chuyển C hiện có sang Rust unsafe rồi mới sửa tiếp
- Mục tiêu là tập con applicative của C bao gồm xử lý dữ liệu, phép toán con trỏ, luồng điều khiển có cấu trúc và mã có tính di động; loại trừ mã phụ thuộc vào
goto, ép kiểu số nguyên-con trỏ, mẹo con trỏ, bitfield và untagged union - Việc chuyển đổi gồm 2 bước: hạ Clang AST xuống Mini-C rồi chuyển sang Rust an toàn, đồng thời làm tường minh các hành vi tinh vi của C như integer promotion, chuyển đổi ngầm định, biểu thức gán và tăng trước/sau
- Phần khó nhất là số học con trỏ, được thay bằng cách phân tách dựa trên Rust slice và
split_at_mut/split_at; đồng thời cũng xử lý suy luận tính khả biến, suy ra trait và lựa chọn giữaBoxvới borrow - Nhóm đã đánh giá trên SymCrypt của Windows, HACL★, thuật toán nén lõi của bzip2, parser/serializer CBOR của EverParse và một phần Microsoft FrodoKEM; trong quá trình chuyển đổi cũng phát hiện undefined behavior ở bzip2 và FrodoKEM
Lộ trình chuyển đổi C→Rust mà Scylla chọn
- Động lực chuyển mã C hiện có sang Rust bắt nguồn từ vấn đề an toàn bộ nhớ
- Nghiên cứu của Google và Microsoft ước tính 70% lỗ hổng bảo mật liên quan đến xử lý bộ nhớ sai
- Doanh nghiệp và chính phủ đang khuyến nghị dùng các ngôn ngữ an toàn bộ nhớ như Rust cho những hệ thống quan trọng về an toàn
- Rust có lợi thế rõ ràng cho mã mới, nhưng rất khó viết lại toàn bộ mã C công nghiệp đã được kiểm thử và debug
- Các công cụ chuyển C→Rust tự động hiện nay phần lớn xuất ra Rust unsafe để hỗ trợ toàn bộ C
- Chúng cho phép các thành ngữ kiểu C như unchecked pointer hay transmutation của Rust
- Khi đó bảo đảm an toàn bộ nhớ tĩnh biến mất, làm suy yếu mục tiêu dùng một ngôn ngữ an toàn
- Quy trình phổ biến là lấy đầu ra Rust unsafe làm điểm khởi đầu, rồi refactor lặp đi lặp lại sang Rust an toàn
- Đã có các phân tích tĩnh được đề xuất để đổi raw pointer sang borrow an toàn của Rust, hoặc khôi phục các trừu tượng Rust từ biểu diễn mức thấp
- Tuy nhiên các công cụ refactor còn rời rạc, và hỗ trợ
c2rust refactorđã dừng từ năm 2022
- Thay vì sửa Rust unsafe đã sinh ra, Scylla chọn cách dần cấu trúc lại chính mã C gốc để có thể biên dịch sang Rust an toàn
Tập con C được hỗ trợ và các mẫu bị loại trừ
- Mục tiêu của Scylla là tạo ra phép chuyển đổi có thể dự đoán được và sinh mã Rust gần với C gốc
- Đối tượng được hỗ trợ là tập con applicative của C
- Mã thao tác và xử lý dữ liệu
- Mã dùng số học con trỏ
- Mã có luồng điều khiển có cấu trúc
- Mã có tính di động
- Những codebase phụ thuộc vào các mẫu sau sẽ không được hỗ trợ
goto- Sử dụng biểu diễn đối tượng thông qua ép kiểu số nguyên-con trỏ
- Mẹo con trỏ
- Bitfield
- Untagged union
- Lập trình viên có thể áp dụng rewrite có chủ đích và annotation lên mã C để Scylla hiểu được
- Có thể viết lại các mẫu aliasing không phù hợp với borrow checker của Rust
- Có thể cung cấp cho Scylla thông tin rằng tagged union nên được dịch thành ADT mức cao
Mini-C: ngôn ngữ trung gian giảm bớt tính mơ hồ của C
- Scylla bắt đầu từ AST frontend của Clang rồi trước tiên chuyển sang một ngôn ngữ tên là Mini-C
- Mini-C xử lý nhánh, vòng lặp, con trỏ, dereference và lấy địa chỉ giống C, nhưng có ngữ nghĩa “no-surprises”
- Mọi số nguyên đều có độ rộng cố định
- Integer promotion và integer conversion của C được biểu diễn bằng cast tường minh
- Không cho phép untyped pointer như
void *
- Khác với C, Mini-C là ngôn ngữ biểu thức
- Phép gán không trả về giá trị
- Các cú pháp C như
e1 = e2 = e3,p[i++]sẽ được desugar trong Mini-C - Biểu thức kiểm tra trong vòng lặp và câu điều kiện phải có kiểu
bool, không phảiintnhư trong C
- Khi chuyển typed AST của Clang sang typed AST của Mini-C, Scylla làm tường minh các hành vi ngầm của C
- Điều chỉnh biểu thức điều kiện về
bool - Điều chỉnh chỉ số mảng về
size_t - Đổi các chuyển đổi ngầm trong đối số gọi hàm và vế phải của phép gán thành cast tường minh
- Phản ánh usual arithmetic conversions của chuẩn C trong các phép toán số học
- Điều chỉnh biểu thức điều kiện về
- Việc chuyển đổi giả định rằng mã C là có tính di động và không phụ thuộc vào mô hình dữ liệu của C
- Ví dụ, không kỳ vọng có mã hoạt động khác nhau tùy
longlà 4 byte hay 8 byte - Ở thời điểm configure, hệ thống dò mô hình dữ liệu của kiến trúc đích và chuyển
unsigned intthành kiểu độ rộng cố định nhưuint32_t
- Ví dụ, không kỳ vọng có mã hoạt động khác nhau tùy
Tổng hợp ADT và tuple
- Mini-C cung cấp ADT, tuple và pattern matching ở mức cao hơn C
- Tagged union được dịch thành ADT thông qua annotation
- Dạng đích là cấu trúc như
{ int tag; union { t0 case0; ...; tn caseN }} - Giả định giá trị tag chạy từ 0 đến N, và giá trị tag khớp với thứ tự các case trong union
- Dạng đích là cấu trúc như
- Scylla chuyển kiểu tagged union đã annotate thành kiểu variant
- Khi tạo giá trị, nó kiểm tra
.tag = ivà.casej = ecó khớp nhau không - Nếu khớp, nó chuyển thành giá trị constructor tương ứng
- Nếu payload và tag không khớp, nó sẽ không dịch sang Mini-C
- Khi tạo giá trị, nó kiểm tra
- Việc truy cập field của tagged union chỉ an toàn khi biết trạng thái tag hiện tại
- Scylla nhận diện các mẫu như
if (x.tag == i) { ... x.casei }hoặcswitch - Sau đó chuyển thành dạng
match x with | Ci v -> ... - Truy cập case khác của union bị xem là invalid và gây lỗi chuyển đổi
- Scylla nhận diện các mẫu như
- Tuple cũng có thể được tổng hợp bằng annotation
- Struct có
nfield sẽ được chuyển thành tuplenngôi - Truy cập field sẽ đổi thành truy cập field của tuple
- Tuple được định kiểu theo cấu trúc nên có lợi thế về mut-polymorphism
- Struct có
Chuyển từ Mini-C sang Rust an toàn
- Mini-C cung cấp biểu diễn chương trình C đầy đủ với annotation kiểu, sau đó được chuyển sang Rust an toàn
- Có ba khó khăn chính
- Loại bỏ số học con trỏ của C
- Làm tường minh tính khả biến và aliasing
- Tự động cung cấp các cấu trúc thành ngữ Rust như trait
- Việc chuyển kiểu con trỏ khá phức tạp vì Rust biểu diễn con trỏ khác C
- Rust phân biệt
Box<T>và&T - Rust cũng phân biệt con trỏ một phần tử và con trỏ nhiều phần tử như
&Tvà&[T] - Mảng trong Rust là giá trị và không tự động decay thành con trỏ như C
- Rust phân biệt
- Chiến lược cơ bản là biên dịch mọi kiểu pointer của C thành slice borrow
&[T]trong Rust- Cả con trỏ trên stack lẫn heap về cơ bản đều thành slice borrow
- Cả con trỏ một phần tử và nhiều phần tử cũng mặc định thành slice borrow
- Tính khả biến sẽ được suy luận tự động ở một bước riêng
- Scylla dùng heuristic và annotation thủ công để dịch một số con trỏ thành
Box<T>- Các hàm như
T *create()được xem là tạo allocation mới, không có tham chiếu toàn cục, có thể được dịch thànhfn create() -> Box<T> - Phân tích này được áp dụng đệ quy theo kiểu fixed-point vào cả định nghĩa struct và variant
- Các struct còn chứa borrow sẽ có lifetime parameter
- Các hàm như
Ràng buộc của chuyển đổi giữa Box, slice và array
- Trong Rust, cần chuyển đổi tường minh giữa mảng, slice borrow và
Box, vì vậy bước chuyển sang Rust của Scylla hoạt động theo hướng kiểu - Các quy tắc chuyển đổi sẽ chèn coercion để đổi mảng hoặc boxed slice thành slice borrow
- Mảng sẽ thành slice borrow theo dạng như
&x[..] - Boxed slice có thể được đổi thành borrow
- Mảng sẽ thành slice borrow theo dạng như
- Cũng có thể chuyển theo chiều ngược lại
- Slice hoặc mảng có thể được nâng thành allocation trên heap để đổi thành
Box<[T]>
- Slice hoặc mảng có thể được nâng thành allocation trên heap để đổi thành
- Việc chuyển ngược này có thể tạo ra khác biệt về ngữ nghĩa sao chép
- Trong C, mảng và con trỏ có thể trỏ cùng một vùng nhớ
- Trong Rust,
Box::new(x)có thể tạo ra một bản sao củax - Các mảng kiểu cơ bản như mảng số nguyên không có cách opt out khỏi trait
Copy, nên Rust có thể âm thầm sao chép
- Khi việc chuyển đổi này xảy ra, Scylla loại biến gốc khỏi môi trường để cấm dùng về sau
- Nếu chương trình C gốc vẫn tiếp tục dùng biến đó, sẽ phát sinh lỗi chuyển đổi
- Lập trình viên cần sửa mã C trước khi chuyển để thể hiện ý định rõ ràng hơn
Biến số học con trỏ thành tách slice trong Rust
- Chương trình C thường không chỉ truy cập mảng qua một base pointer duy nhất, mà còn chia mảng thành chunk hoặc duy trì con trỏ vị trí hiện tại để duyệt
- Rust không cho phép số học con trỏ tùy ý, mà cung cấp cách tách slice bằng
split_at_muthoặcsplit_atsplit_at_mutlà primitive từ bỏ quyền sở hữu slice gốc để lấy hai sub-slice- Điều này giữ được bất biến của Rust rằng dữ liệu mutable phải có một owner duy nhất
- Để khớp số học con trỏ của C với cách phân tách trong Rust, Scylla đưa vào split tree
- Mỗi con trỏ C được ánh xạ tới một split tree
- Split tree thay đổi phụ thuộc vào luồng điều khiển
- Nó theo dõi tại một điểm chương trình cụ thể thì truy cập con trỏ C phải được đổi thành truy cập slice Rust nào
- Vì con trỏ C không có thông tin độ dài, Scylla giả định các chunk không chồng lấp lên nhau
- Nếu chủ đích là chồng lấp, kiểm tra kiểu của Rust sẽ không cho qua và lập trình viên phải viết lại mã C
- Để phép chuyển đổi có thể dự đoán được, hệ thống tránh backtracking và xử lý theo hướng tiến
- Trong ví dụ, mảng 32 byte
abcdđược chia thành bốn vùng limb, mỗi vùng 8 byte- Trong C, người ta dùng số học con trỏ như
abcd + 0,abcd + 16,abcd + 8,abcd + 24, không theo thứ tự trái sang phải - Bản dịch Rust lưu lịch sử gọi
split_at_muttrong split tree để tìm đúng sub-slice
- Trong C, người ta dùng số học con trỏ như
Các đối tượng đánh giá và undefined behavior được phát hiện
- Triển khai Scylla dùng Clang để nhận đầu vào là mã C hiện có và xuất ra Rust an toàn
- Tập đánh giá bao gồm một phần của nhiều dự án C hiện có
- Một phần của SymCrypt trên Windows
- Một phần thư viện mật mã HACL★
- Phần lõi của thuật toán nén bzip2
- Parser và serializer nhị phân CBOR của thư viện EverParse
- Triển khai primitive mật mã hậu lượng tử FrodoKEM của Microsoft
- Các trường hợp này cho thấy tập con applicative của C mà Scylla nhắm tới có thể bao phủ nhiều ứng dụng nhạy cảm về bảo mật
- Trong quá trình chuyển đổi, nhóm cũng nhận diện và báo cáo undefined behavior tồn tại trong mã C gốc của bzip2 và FrodoKEM
1 bình luận
Các ý kiến trên Hacker News
Điều quan trọng là công trình này nhắm tới “các codebase C đã được kiểm chứng hình thức”
Mã C hệ thống nói chung không được kiểm chứng hình thức, nên đó là một câu chuyện khá khác
Ví dụ, một con trỏ trỏ tới mảng C trên stack có thể được dịch trong Rust thành
Box<[u8]>, giống như một con trỏ sở hữu bản sao mới trên heap. Nếu mã gốc phụ thuộc vào việc con trỏ thực sự trỏ tới mảng đó, mã đã dịch có thể âm thầm hoạt động saiVới tính năng tự động dịch tập con C++ an toàn bộ nhớ trong dự án scpptool của tôi, có lẽ nó sẽ xử lý bằng cách chuyển mảng sang các kiểu thay thế và iterator để giữ nguyên ngữ nghĩa ban đầu
Dự án của OP có thể chỉ xử lý những đoạn C dễ chuyển sang Rust an toàn, nhưng xét độ khó của vấn đề thì kết quả vẫn đáng được tôn trọng và cũng cho thấy một mức hữu ích nhất định
Ngay từ đầu, họ không dịch C thực tế, mà sửa để mã viết bằng F* được phía compiler C xuất ra Rust. Họ không đối mặt với mã C thực tế phức tạp, cùng lắm chỉ xử lý một Mini-C bị giới hạn kiểu như thứ mà một compiler đồ chơi có thể phát ra
Ngay trong bài gốc cũng nói rằng nếu chương trình C ban đầu phụ thuộc nhiều hơn vào
xthì bản dịch sẽ báo lỗi và yêu cầu lập trình viên sửa mã nguồn; điều đó có nghĩa là họ mong C đã được viết sẵn theo phong cách làm hài lòng borrow checker của RustNó giống kiểu diễn đạt học thuật “Hình 4 trình bày các quy tắc đẹp đẽ, nhưng triển khai thực tế dựa vào vô số mẹo”
Tệ hơn nữa, họ nói các trường hợp chồng lấp có thể phân biệt tĩnh sẽ gây lỗi biên dịch, còn nếu không thì mã Rust có thể panic khi chạy. Việc biến một chương trình C đã được kiểm chứng hình thức thành một chương trình Rust “giờ có thể crash” là khá kỳ lạ
Gọi HACL* là một codebase C đã được kiểm chứng hình thức hiện có cũng không chính xác. HACL* tuy được biên dịch sang C, nhưng không phải là thư viện C mà được viết bằng một ngôn ngữ hoàn toàn khác
Tiêu đề trung thực đáng lẽ nên là “Biên dịch một tập con của F* sang Rust an toàn một phần, được hình thức hóa một phần”
Năm 2002, các nhà nghiên cứu đã công bố bài báo về Cyclone, một phương ngữ an toàn của C, và khi port thủ công mã C sang Cyclone, họ đã phát hiện các lỗi an toàn trong mã C hiện có
Những chuyển đổi C kiểu thủ công hoặc tự động như vậy không chỉ có tiềm năng tăng mức độ chấp nhận các ngôn ngữ an toàn hơn, mà còn giúp lộ ra các bug hiện có
[1] https://www.researchgate.net/profile/James-Cheney-2/publicat...
Nhiều ý tưởng của Cyclone đã đi vào Rust, và mã vẫn có thể làm cho chạy được nếu bỏ công sức, nhưng không build ngay được trên các nền tảng 64-bit hiện đại
http://cyclone.thelanguage.org
Tôi đã thử port một vài dự án, gồm cả dự án C, sang Rust bằng C2Rust làm bước đầu tiên, và rút ra vài kết luận
unsafe, các ràng buộc mạnh của Rust, chẳng hạn kiểm tra biên và signature nghiêm ngặt, thường khiến bug lộ ra nhanh chóngDù vậy, công cụ là thiết yếu cho việc port, và công cụ càng tiến bộ thì quá trình sẽ càng trơn tru hơn
Chỉ cần biểu diễn bộ nhớ như một mảng và coi con trỏ là chỉ số trong mảng đó. Khi đó có thể biểu diễn các hành vi của C như số học con trỏ không kiểm tra hay union mà không phải đấu với borrow checker, đồng thời vẫn bảo toàn ngữ nghĩa. Trong C→Java, kỹ thuật tương tự đã được dùng từ lâu
Tất nhiên giá trị của kiểu chuyển đổi này khá mơ hồ. Về cơ bản nó giống biên dịch C sang wasm nhưng chậm hơn, và dù mã kết quả về mặt kỹ thuật là “an toàn”, các vấn đề vẫn còn: buffer overflow có thể tạo ra trạng thái sai, hoặc dangling pointer có thể cho phép truy cập dữ liệu trong ngữ cảnh lẽ ra không được phép
Tôi là tác giả. Có lẽ sẽ hữu ích nếu tổng hợp vài điểm từ nhiều luồng thảo luận
unsafe RustĐiều tôi thật sự thắc mắc là tại sao phải làm theo hướng này
Nếu đây là công nghệ có thể thật sự chuyển đổi ứng dụng công nghiệp từ C sang Rust, thì có vẻ cũng có thể giúp bọc thép các ứng dụng C hiện có dễ hơn. Chỉ cần tạo ra các phân tích để đưa vào công cụ hiện có như bộ phân tích tĩnh hoặc trình sinh test
Tương tự, cũng có thể sinh wrapper an toàn để cho phép viết mã mới bằng Rust bên cạnh phần C đã được kiểm chứng. Mã mới hưởng lợi từ Rust, mã cũ được xác nhận là an toàn, và giao diện cũng trở nên an toàn hơn
Một trình dịch hoàn chỉnh có thể là lý tưởng. Vì về lâu dài, codebase dùng một ngôn ngữ vẫn tốt hơn. Nhưng với C/C++ hiện có, nhu cầu lớn nhất vẫn là bảo đảm an toàn chỉ bằng một nút bấm với ít cảnh báo sai. Cũng có thể tự động sửa các cấu trúc xấu ngay trong C, giống công cụ compiler của Google hay Mayhem của ForAllSecure
Có thể vì chúng dựa vào hành vi không xác định hoặc hành vi không được đặc tả, hoặc vì nếu thêm các kiểm tra an toàn phù hợp thì miền đầu vào được chấp nhận sẽ bị thu hẹp quá nhiều đến mức vô dụng
Dịch sang một ngôn ngữ an toàn trong các trường hợp như vậy khách quan là tốt hơn, vì có thể giữ được sức biểu đạt của đầu vào trong khi bảo đảm tĩnh hành vi đúng khi chạy
Khái niệm “C đã được chứng minh ngoài thực địa” cũng khó có thể coi là tồn tại, như vô số lỗ hổng nghiêm trọng đã cho thấy. Thứ thực sự tồn tại chỉ là C hoạt động đủ tốt, đủ thường xuyên để trông có vẻ hữu ích
Mã cũ thường chỉ được giả định là an toàn nhờ may mắn, chứ không được chứng minh. “Chứng minh” có ý nghĩa cụ thể, nhất là trong ngữ cảnh các bài báo như thế này, và đại đa số mã C không được chứng minh theo tiêu chuẩn toán học nghiêm ngặt. Trong khi đó, hệ thống kiểu của Rust đã được chứng minh là đúng về mặt toán học
Một trình dịch toàn bộ phụ thuộc vào việc có thể từ bỏ điều gì. Nếu chấp nhận hy sinh hiệu năng, miền đầu vào, phạm vi đầu ra, độ dễ đọc của mã, v.v. thì có thể làm được ở mức nào đó; nhưng khi bắt đầu muốn một trình dịch vừa sound vừa complete trên mọi phương diện này thì vấn đề sẽ xuất hiện
Nếu dịch ngây thơ sang Rust thì chẳng phải sẽ trộn lẫn phần an toàn và phần
unsafesao? Khi đó công việc thủ công có vẻ chỉ cần kiểm tra tính an toàn của các vùngunsafe. Gần giống như khi viết Rust từ đầuNếu 90% kết quả không phải
unsafethì có vẻ đã là lợi ích khá lớnunsafe Rustcấp thấpOpenJPEG vốn được biết là segfault với một test case nhất định, và khi chạy test đó trên bản Rust thì nó segfault ở đoạn mã Rust tương ứng cùng vị trí. Ít nhất là tương thích
Nhưng cách tiếp cận đó là ngõ cụt. Muốn tiến lên, trình dịch phải nhận diện các idiom phổ biến của C và nâng chúng lên dạng tự nhiên của ngôn ngữ đích. “Biên dịch” sang Rust sẽ tạo ra thứ Rust kinh khủng, đầy các lời gọi hàm thao tác con trỏ kiểu C không an toàn
Vấn đề nâng cấp lớn nhất phần lớn liên quan đến con trỏ. Thành quả hứa hẹn nhất của bài báo này là đã tìm ra cách thay số học con trỏ C bằng slice của Rust. Slice có thể làm hầu hết những gì số học con trỏ C làm, và giờ đã có người tự động hóa bản dịch đó. Những phép toán con trỏ không thể dịch được nên bị nhìn nhận rất đáng nghi
Sẽ hữu ích nếu nghĩ rằng con trỏ thô trỏ tới mảng trong C ngầm có kèm độ dài. Độ dài đó không hiện ra trong mã nguồn C, nhưng tồn tại đâu đó như một hàm của trạng thái chương trình. Nó có thể là hằng số, kích thước yêu cầu
malloc, hoặc tham số hàm. Với lập trình viên bảo trì, việc tìm độ dài mảng thường không quá khóĐây có thể là bài toán phù hợp với LLM. Kiểu như hỏi “hãy nhìn đoạn mã này và tìm độ dài của mảng
foolà bao nhiêu”, rồi để một trình dịch không phải LLM dùng câu trả lời đó để dẫn hướng chuyển đổi sang Rust. Nếu LLM sai, Rust sẽ báo lỗi chỉ mục hoặc có mảng quá lớn, nhưng sẽ không mất an toàn. Các idiom biểu diễn thông tin kích thước mảng trong C đủ khuôn mẫu để phần lớn có thể đoán đúng. Đặc biệt, LLM còn có thể đọc cả chú thíchunsafe. Vì nó sẽ dùng con trỏ thô khắp nơi thay vì referenceMã C không được viết với mô hình aliasing và các ràng buộc của borrow checker trong Rust, nên rất khó dịch thành reference
Chỉ là biên dịch một tập con rất nhỏ của C mà thôi. Trên thực tế có thể nhỏ đến mức vô dụng
Tôi không kỳ vọng nhiều vào cách tiếp cận này. Chắc chắn sẽ đụng phải giới hạn của những gì có thể làm bằng phân tích tĩnh mã C. Hơn nữa, việc chọn Rust làm đích khiến vấn đề khó một cách không cần thiết. Mô hình sở hữu của Rust quá khác với cách các chương trình C thực tế vận hành
Ví dụ, buffer trong C đương nhiên có độ dài, nhưng trong C độ dài không được gắn tường minh với con trỏ. Vì vậy bộ dịch phải suy luận chương trình C theo dõi độ dài như thế nào rồi chuyển thành slice. Ngay cả khi độ dài là một biến tường minh cũng không dễ, và sẽ còn rắc rối hơn nếu độ dài được tính toán hoặc được biểu diễn dưới dạng “con trỏ sau phần tử cuối”
Các pattern C như
bool should_free_this_pointercũng có thể chuyển sang enumOwned/Borrowedcủa Rust, nhưng phải suy luận allocation nào liên kết với boolean nào, và phạm vi an toàn thực sự của biến thể borrowed nằm ở đâuDù vậy tôi đồng ý rằng tạo Rust đúng idiom từ C tùy ý sẽ rất khó. Có thể nói chỉ đạt mức “đại khái đúng”
Tôi tò mò việc này so với tính năng chuyển đổi C của Zig như thế nào
Zig có vẻ rất giỏi trong việc tạo môi trường hỗn hợp: mã mới viết bằng Zig, mã cũ giữ bằng C, thực hiện chuyển đổi hoặc tương tác, và thậm chí đóng vai trò trình biên dịch C
Chắc hẳn có lý do rất tốt khiến các maintainer nhân Linux không xem Zig là phương án thay thế C thay vì Rust. Tôi không biết đủ để suy đoán, nên mong những người hiểu rõ hơn giải thích
Tôi không phải maintainer kernel, nhưng nếu đoán hai lý do lớn khiến Rust được chọn hơn Zig, có thể là vì ngôn ngữ này cung cấp đảm bảo tại thời điểm biên dịch tốt hơn và tốc độ chấp nhận cũng nhanh hơn
Các công ty lớn trong ngành đang làm rất nhiều để cung cấp mã Rust native cho API hoặc các binding Rust được duy trì. Các nhà phát triển Windows cũng đang viết lại một phần kernel của họ bằng Rust. Đây là một chuyển động đã kéo dài khá lâu, và tôi hy vọng nó không dừng lại
Các maintainer có thể cảm thấy Zig không đem lại đủ lợi ích so với C. Nhiều người trong số họ vẫn còn phản đối cả Rust
Zig có khả năng tương tác với C tốt hơn Rust rất nhiều, nhưng không an toàn bộ nhớ và cũng chưa ổn định. Việc Zig được chấp nhận trong thế giới C có thể sẽ tăng đáng kể, nhưng khó xem là cạnh tranh trực tiếp với Rust
Ở khu vực của tôi, không ai áp dụng Rust, còn những người C++ vẫn ở lại với C++. Ban đầu cũng có chút quan tâm đến Rust, nhưng nó không bám rễ được ở bất kỳ công ty nào tôi biết. Có thể vì lý do tương tự như việc Go tăng trưởng mạnh ở các công ty trẻ nhưng không thâm nhập tốt vào các công ty Java/C# truyền thống. Dù có hợp lý về mặt kỹ thuật, đó vẫn là một bài toán quản lý thay đổi khổng lồ
Zig đang có đà ở phía các chương trình không cần cấp phát bộ nhớ động, nhưng ngoài đó ra thì chưa nhiều
Vẫn thường xuyên có các thay đổi phá vỡ tương thích; điều đó tốt cho Zig hiện tại, nhưng không tốt cho một codebase khổng lồ và sống lâu như Linux. Lỗi trình biên dịch cũng xuất hiện
Tôi nói vậy dù nhìn chung thích hướng đi của Zig
Tôi tò mò liệu một công cụ như
C2Rustcó thể tận dụng việc này để tạo ra mã đúng về mặt hình thức hay khôngTôi cũng tò mò các tác giả đã làm thủ công đến mức nào, hay họ có chạy thứ gì đó để sinh mã Rust không. Nếu có thì tôi không biết mã tạo ra Rust nằm ở đâu, và cũng không thấy liên kết tới kho nguồn
Nếu thư viện C hoạt động được, tức không được chứng minh hình thức là không có vấn đề nhưng phần lớn chạy tốt, thì tôi tò mò tại sao không dùng
unsafe Rustđể dịchNhìn chung Rust còn thiếu thư viện, nên tôi thấy việc này có giá trị. Rốt cuộc cũng không khác nhiều so với việc dùng dll/so viết bằng C, vốn có thể không an toàn trong một số tình huống