Viết lại Bun bằng Rust
(bun.com)- Bun, khởi đầu bằng Zig, đã phát triển thành runtime được tải xuống hơn 22 triệu lần mỗi tháng, nhưng việc kết hợp engine JavaScript dựa trên GC với quản lý bộ nhớ thủ công đã gây ra các vấn đề ổn định lặp lại, trở thành động lực để chuyển sang Rust
- Thay vì để con người chuyển 535.496 dòng mã Zig trong 1 năm, nhóm đã chạy song song khoảng 50 workflow động của Claude Code và tối đa 64 instance Claude trong 11 ngày
- Quá trình port được kiểm chứng bằng
PORTING.md,LIFETIMES.tsv, 1 người triển khai cùng ít nhất 2 reviewer đối kháng, và bộ test TypeScript hiện có; tất cả đều pass 100% trên CI của 6 nền tảng - Sau khi chuyển sang Rust, Bun v1.4.0 đã sửa 128 lỗi tái hiện được ở v1.3.14, khắc phục toàn bộ rò rỉ bộ nhớ có thể instrumentation, và giảm kích thước binary trên Linux·Windows khoảng 20%
- Bun v1.3.14 là phiên bản Zig cuối cùng, còn v1.4.0 là phiên bản Rust đầu tiên được cung cấp trên canary; nhóm dùng borrow checker, Miri, LeakSanitizer và fuzzing dựa trên coverage 24/7 làm công cụ cải thiện độ ổn định
Bun khởi đầu bằng Zig và vấn đề ổn định
- Bun bắt đầu là dự án port từng dòng transpiler JavaScript·TypeScript của esbuild từ Go sang Zig
- Mã Zig đầu tiên được viết vào ngày 16/4/2021, và khả năng kiểm soát cấp thấp cùng thiết kế hướng hiệu năng của Zig đã giúp hiện thực hóa bản triển khai Bun ban đầu
- Bun giai đoạn đầu được một người viết bằng Zig trong 1 năm, với phạm vi rất rộng
- Transpiler·minifier·bundler cho JavaScript, TypeScript, CSS
- Trình quản lý package tương thích npm
- Test runner tương tự Jest
- Phân giải module tương thích Node.js·TypeScript
- Client HTTP/1.1·WebSocket
- Triển khai các API Node.js như
fs,net,tls
- Hiện Bun CLI được tải xuống hơn 22 triệu lần mỗi tháng, được Claude Code và OpenCode dùng làm runtime, và được Vercel, Railway, DigitalOcean cùng các bên khác hỗ trợ first-party
Các lỗi an toàn bộ nhớ lặp lại
- Các ví dụ lỗi được sửa trong Bun v1.3.14 bao gồm use-after-free, double-free, rò rỉ bộ nhớ, truy cập out-of-bounds và race condition
- Heap-use-after-free do gọi
.reset()trong lúc async.write()củanode:zlib - Use-after-free trong
node:http2khi callback JS tái nhập gây rehash hashmap, làm vô hiệu hóa con trỏ stream nội bộ - Vấn đề callback
valueOf()hoặctoString()detachArrayBuffertrongUDPSocket.send()vàsendMany() - Crash và out-of-bounds read trong
Buffer#copy,Buffer#filldoArrayBufferbị detach hoặc resize trong quá trình coercion đối số - Rò rỉ bộ nhớ liên quan đến
crypto.scrypt,tlsSocket.setSession(),fs.watch() - Double-free khi CSS parser xử lý vendor prefix và background nhiều lớp
- Crash do race condition
MessageEventkhi truy cập đồng thờiBroadcastChannelhoặcMessagePort
- Heap-use-after-free do gọi
- Trước đây nhóm cũng đã dùng nhiều cơ chế để tăng cường độ ổn định
- Patch hỗ trợ Address Sanitizer vào Zig compiler và chạy bộ test ASAN trên mọi commit
- Phát hành bản build ReleaseSafe có kiểm tra an toàn của Zig cho Windows
- Fuzz API runtime của Bun 24/7 bằng Fuzzilli
- Vận hành nhiều test rò rỉ bộ nhớ end-to-end
- Lập trường không phải Zig tự thân là vấn đề; nguồn chính của vấn đề ổn định là yêu cầu phải xử lý đồng thời giá trị GC và bộ nhớ quản lý thủ công
Lý do chọn Rust
- JavaScript là ngôn ngữ GC, và các engine như JavaScriptCore và V8 có quy tắc nghiêm ngặt về xử lý exception và GC
- Zig không tự động quản lý bộ nhớ như C, không có constructor·destructor, và cleanup phần lớn phải được chỉ định rõ bằng
defertại từng call site - Trong Bun, việc xử lý đúng lifetime của giá trị GC và giá trị quản lý thủ công là một nguồn lớn gây ra vấn đề ổn định
- Phải xác nhận byte đã cấp phát được giải phóng ở đâu
- Phải đảm bảo chỉ được giải phóng một lần
- Phải kiểm tra đúng cách việc xử lý exception JavaScript
- Phải xác nhận con trỏ GC có hiển thị với conservative stack scanner hay không
- Cách cleanup của Zig là
defer,errdefertường minh; C++ dùng destructor và move, còn Rust dùngDrop - Trong mã Zig hiện có của Bun, arena lifetime, reference counting và review cẩn thận được kết hợp với nhau
- Cũng có thể cưỡng chế quy tắc ownership bằng style guide và code review, nhưng trong mã Rust an toàn, use-after-free, double-free và free bị thiếu trên error path sẽ trở thành lỗi trình biên dịch
- Khoảng 20% mã của Bun là C++ và nhúng nhiều thư viện C/C++
- JavaScriptCore
- uWebSockets và usockets
- lshpack và lsquic
- BoringSSL
- SQLite
- C++ cũng có thể là một lựa chọn, nhưng vẫn phụ thuộc vào style guide và code review; ngay cả khi có ASAN, hỏng bộ nhớ và rò rỉ vẫn có thể xảy ra
Chiến lược viết lại: một lần, theo kiểu cơ học
- Mã Zig hiện có của Bun có 535.496 dòng không tính chú thích, và một lần viết lại theo cách truyền thống được đánh giá là công việc mất khoảng 1 năm với một nhóm kỹ sư nhỏ
- Vì không thể dừng sửa lỗi, vá bảo mật và phát triển tính năng trong 1 năm, port cơ học nhằm giảm thiểu thay đổi hành vi với người dùng được chọn làm cách tiếp cận ít rủi ro nhất
- Bộ test của Bun được viết bằng TypeScript nên không phụ thuộc vào ngôn ngữ triển khai runtime
- Viết lại tăng dần được đánh giá là đau đớn trong ngắn và trung hạn vì phải tạo mã tạm và kỳ vọng sẽ xóa sau này, nên nhóm chuyển toàn bộ một lần
- Mã Rust được viết để trông như được transpile từ mã Zig, và hướng đi được chọn là sau Bun v1.4 sẽ giảm dần
unsaferồi refactor theo Rust idiomatic
Workflow động của Claude Code
- Trong lần viết lại bằng Rust, khoảng 50 workflow động trên Claude Code đã chạy liên tục trong 11 ngày
- Các workflow trải dài từ viết hướng dẫn port, chuyển đổi file, sửa lỗi biên dịch, khôi phục subcommand, vượt qua toàn bộ test, cho đến cleanup quy mô lớn
- Tạo hướng dẫn port ánh xạ các pattern và type của Zig sang pattern và type của Rust
- Port cơ học mọi file
.zigthành file.rstheoPORTING.mdvàLIFETIMES.tsv - Sửa lỗi compiler theo từng crate
- Khôi phục hoạt động của các subcommand như
bun test,bun build - Pass toàn bộ bộ test
- Refactor và cleanup quy mô lớn
- Trong phần lớn thời gian, con người đọc đầu ra của workflow, kiểm tra vấn đề và lỗi, rồi điều chỉnh prompt để Claude sửa vòng lặp
- Trong phần chuẩn bị, nhóm đã thảo luận khoảng 3 giờ với Claude về cách ánh xạ các pattern trong codebase Zig sang Rust, và kết quả được tuần tự hóa thành
PORTING.md - Để thêm Rust lifetime vào mã quản lý bộ nhớ thủ công, nhóm chạy workflow phân tích lifetime của mọi struct field
- Tìm các field có lifetime phức tạp
- Đề xuất lifetime
- 2 agent review đối kháng kiểm tra
- Phản hồi được phản ánh và lưu vào
LIFETIMES.tsv
Phương thức review đối kháng
- Với mỗi Claude triển khai, đặt một Claude reviewer đối kháng trong một context window riêng; reviewer chỉ nhận diff và được chỉ thị giả định rằng code sai để tìm bug
- Cấu trúc cơ bản gồm 1 người triển khai, 2 reviewer đối kháng trở lên và 1 fixer
- Tất cả bug mà reviewer thực sự bắt được đều vượt qua biên dịch nhưng có vấn đề về hành vi
uv_closelà bất đồng bộ nhưngBox<uv::Pipe>bị drop ở cuối match arm, khiến libuv giữ freed memory, dẫn đến use-after-free và double-free- Lỗi timespec khi dùng
trunc()với file time âm không nguyên, tạo ransecâm - Lỗi
unwrap_orđánh giá eager đối số, gây panic trong trường hợp lược bỏ percentage củacolor-mix()
- Giống như review của con người, tách context của tác giả và reviewer để giảm thiên lệch có thể phát sinh do người triển khai muốn merge
Thực thi port quy mô lớn và song song hóa
- Trước khi chuyển toàn bộ 1.448 file
.zig, họ xác minh quy trình trước bằng 3 file- 1 người triển khai viết file
.rs - 2 reviewer kiểm tra xem hành vi có khớp với
.zigvà có tuân theoPORTING.md,LIFETIMES.tsvhay không - 1 fixer áp dụng các đề xuất
- 1 người triển khai viết file
- Ở giai đoạn đầu port toàn bộ file, nhiều Claude chạy
git stash,git stash pop,git reset HEAD --hardvà xung đột lẫn nhau - Sau đó họ thêm vào workflow các quy tắc cấm
git stash,git reset, các lệnhgitkhông phải commit file cụ thể, và các lệnh chậm nhưcargo - Cuối cùng, họ dùng 4 workflow shard và 4 worktree; trong mỗi shard, 16 Claude commit và push file
- Nhờ song song hóa và chuẩn bị trước, ở mức peak Claude viết khoảng 1.300 dòng code mỗi phút
- Nhánh port có 6.502 commit không tính các commit merge, giờ peak đạt 695 commit, và landed diff cuối cùng là +1.009.272 dòng
- Do không tăng IOPS mặc định của instance EC2, chỉ một lệnh
grepchậm cũng khiến đọc/ghi đĩa bị dừng trong vài phút
Lỗi biên dịch và tách crate
- Sau khi viết xong toàn bộ code, workflow Claude sửa lỗi compiler
- Codebase Zig thực chất là một compilation unit duy nhất, còn code Rust dự kiến được chia thành khoảng 100 crate để biên dịch nhanh hơn
- Nhóm lỗi khó nhất là phụ thuộc vòng
- Chỉ PR tách crate ngay trước khi viết lại bằng Rust là chưa đủ
- Một workflow riêng phân loại và ghi lại nên đặt code có phụ thuộc vòng ở đâu
- Một workflow khác thực hiện phần refactor đó
- Sau khi xử lý phụ thuộc vòng, khoảng 16.000 lỗi compiler lộ ra
- Các lỗi này được xử lý song song theo từng crate
- Chạy
cargo checktrong từng crate - Gom output theo file và lưu lại
- Sửa lỗi biên dịch của crate đó
- 2 reviewer đối kháng review thay đổi
- 1 fixer áp dụng sửa đổi
- Chạy
- Cũng có một false start khi Claude diễn giải “hãy làm cho tất cả crate biên dịch được” thành việc tạo function stub
- Khi xuất hiện pattern biện minh workaround bằng các chú thích giải thích dài, họ thêm quy tắc review: “nếu cần comment dài cỡ một đoạn văn thì code đã sai và cần sửa code”
Quá trình đến khi pass test
- Sau khi
cargo checkpass, họ lần lượt xử lý lỗi link, panic ngay sau khi khởi động, chạybun --version, rồibun test <file> - Họ dùng workflow lưu stacktrace thất bại theo từng CLI subcommand vào file, rồi sửa qua vòng lặp triển khai–reviewer–fixer
- Workflow cho file test shard khoảng 100 file test ngẫu nhiên vào 4 worktree, lưu stacktrace và lỗi theo từng thất bại rồi sửa
- Bộ test có các bài kiểm tra rò rỉ bộ nhớ và test tích hợp có thể timeout trong debug build
- Test chạy
next devvà hot module reloading phát hiện 100 thay đổi - Stress test làm cạn số lượng TCP socket tối đa
- Test đọc/ghi đĩa ở quy mô gigabyte
- Test spawn khoảng 10.000 process
- Test chạy
- Để cô lập, họ dùng
systemd-runvà cgroups để giới hạn mức dùng bộ nhớ/CPU và tách pid namespace - Dù vậy, máy vẫn crash nhiều lần do hết dung lượng đĩa
- Hai ngày sau lần chạy CI đầu tiên, số file test thất bại giảm từ 972 xuống 23; một ngày rưỡi sau đó Linux hoàn toàn green
- Cuối cùng, toàn bộ test CI trên 6 nền tảng đều pass
- macOS x64
- macOS arm64
- Linux x64
- Linux arm64
- Windows x64
- Windows arm64
- Sau khi test pass 100%, con người kiểm tra thủ công rằng test thực sự được chạy và không bị skip, rồi merge
- Thời điểm merge vào
mainkhông phải là một versioned release; họ chưa đủ tự tin để release, nhưng đã đủ tự tin để dồn sức cho rewrite
Quy mô test và chi phí
- Trong 11 ngày, từ ngày 3/5 đến khi merge ngày 14/5, 6.778 commit được tạo ra
- Test không bị xóa hoặc skip
- Quy mô test theo nền tảng như sau
- Debian 13 x64:
expect()1.386.826 lần, 60.624 test, 4.174 file - macOS 14 arm64:
expect()1.259.953 lần, 58.850 test, 4.175 file - Windows 2019 x64:
expect()1.007.544 lần, 57.337 test, 4.173 file
- Debian 13 x64:
- Công việc pre-merge dùng 5,9 tỷ uncached input token, 690 triệu output token và 72 tỷ cached input token read
- Chi phí theo giá API vào khoảng 165.000 USD
- Họ đánh giá rằng nếu con người tự làm thì 3 kỹ sư có context toàn bộ codebase sẽ mất khoảng 1 năm
- Model được dùng là pre-release Claude Fable 5, và có disclosure rằng Bun đã được Anthropic mua lại vào tháng 12/2025
Review bảo mật, fuzzing và tình trạng unsafe
- Sau khi merge bản port Rust, họ hoàn tất 11 vòng review bảo mật bằng Claude Code Security và xử lý các finding
- Fuzzing dựa trên coverage chạy 24/7 được thêm cho tất cả parser của Bun
- JavaScript
- TypeScript
- JSX
- CSS
- JSON5
- JSONC
- TOML
- YAML
- Markdown
- INI
- Bun Shell scripts
- semver ranges
- file
.patch - CSS colors
- Fuzzer gửi bug tìm được cho Claude để Claude nộp PR bao gồm tái hiện và bản sửa; con người review PR
- Đến nay, parser đã được chạy 100 tỷ lần và dẫn tới khoảng 15 PR
- Tại thời điểm viết, khoảng 4% code Rust nằm trong block
unsafe- Khoảng 13.000 keyword
unsafe - Khoảng 27.000 dòng / tổng khoảng 780.000 dòng
- 78% block
unsafelà một dòng, là con trỏ đến từ C++ hoặc lời gọi thư viện C
- Khoảng 13.000 keyword
- Họ cho biết vì vẫn tiếp tục dùng các thư viện C/C++ như JavaScriptCore, lượng
unsafesẽ luôn nhiều hơn so với dự án Rust thuần
Regression được phát hiện sau khi chuyển sang Rust
- Rust rewrite là một thay đổi quy mô lớn nên đã tạo ra 19 regression đã biết, và tất cả đều đã được sửa
- Phần lớn đến từ những đoạn code có cú pháp tương tự giữa hai ngôn ngữ nhưng ngữ nghĩa khác nhau
-
Side effect bên trong
debug_assert!assertcủa Zig là hàm nên các đối số được thực thi trong mọi builddebug_assert!của Rust là macro nên trong release build, toàn bộ biểu thức bị loại bỏ- Lời gọi
insert_stalebiến mất trong release build, làm hỏng một số trường hợp HMR cụ thể của dự án HTML route dùng React - Issue liên quan: #30678
-
Slice có độ dài lẻ
- Zig helper
reinterpretSlice(u16, bytes)của Bun dùng@divTruncnên đã bỏ qua trailing odd byte bytemuck::cast_slicecủa Rust sẽ panic khi độ dài là lẻ- Đã có regression khiến
Blob.text()với các byte lẻ sau UTF-16 BOM không trả về chuỗi mà làm process panic - Cách sửa là bỏ qua odd byte trở lại bằng
&buf[..buf.len() & !1] - Issue liên quan: #31188
- Zig helper
-
Bounds checks
- Code Zig trên macOS và Linux được compile bằng
ReleaseFastnên bounds check bị loại bỏ, còn Rust release build vẫn giữ bounds check - Kích thước overflow block của Bun module resolver vẫn để placeholder
64, làm ceiling giảm từ 8,4 triệu interned filenames xuống 270.272 - Lỗi off-by-one
ptrs[4095]được port sang trở nên có thể chạm tới trong dự án thực tế, và Rust panic thay vì ghi out-of-bounds - Issue liên quan: #31503
- Code Zig trên macOS và Linux được compile bằng
-
Format string
comptime- Trong
Output.prettycủa Zig,fmtlàcomptimenên các color marker<r>,<d>được chuyển thành ANSI escape trước khi thay thế đối số - Hàm Rust không có comptime parameter nên xử lý marker trên chuỗi đã hoàn chỉnh, và rewrite nhầm cả đối số
- Trong
bun update -i, OSC 8 hyperlink termination xung đột với trailing marker<r>, khiếnrđược in ra như văn bản - Với Rust cần dùng macro
bun_core::pretty!("<r>{}<r>", hyperlink) - Issue liên quan: #30693
- Trong
Các bug đã sửa và rò rỉ bộ nhớ
- Bun v1.4.0 sửa 128 bug có thể tái hiện trong v1.3.14
- Phạm vi bao gồm rò rỉ bộ nhớ, crash, cho đến help text bị tô màu sai
Dropcủa Rust tự động gọi hàmdropkhi giá trị ra khỏi scope- Trong Zig, phải thêm
deferở từng call site, nên dễ bị thiếu cleanup hoặc cleanup trùng lặp Dropcủa Rust là lựa chọn chấp nhận hidden control flow để đổi lại việc giảm các footgun phổ biếnDropđã sửa nhiều rò rỉ bộ nhớ liên quan đến file path trong error handling code- Tích hợp LeakSanitizer của Bun được cải thiện, theo dõi tất cả native code memory allocations
- Tất cả instrumentable memory leak đều đã được sửa
-
Cải thiện rò rỉ trong
Bun.build()- Trong Bun v1.3.14 trước đây, mỗi lần gọi in-process
Bun.build(), parsed source text và AST symbol table tồn tại lâu hơn vòng đời build, gây rò rỉ vài MB mỗi lần - Trong bài test bundle cùng một dự án 60 module 2.000 lần trong một process, v1.3.14 tiếp tục rò rỉ khoảng 3MB mỗi build
- Trong Bun v1.4.0, mức sử dụng bộ nhớ đi ngang
- | Builds | Bun v1.3.14 | Bun v1.4.0 |
- | --- | ---: | ---: |
- | 500 | 1.914 MB | 526 MB |
- | 1.000 | 3.506 MB | 586 MB |
- | 1.500 | 5.097 MB | 608 MB |
- | 2.000 | 6.745 MB | 609 MB |
- Trong Bun v1.3.14 trước đây, mỗi lần gọi in-process
Kích thước binary, mức dùng stack, hiệu năng
- Chỉ với thay đổi ban đầu của Rust rewrite, kích thước binary đã giảm
- Windows: giảm 3,8 MB
- macOS: giảm 5,5 MB
- Linux: giảm 6,8 MB
- Nguyên nhân chính là code Zig đã dùng
comptimequá nhiều - Sau đó cũng áp dụng identical code folding, loại bỏ unused data của ICU, và cách lazy decompress một phần libicu bằng zstd dictionary
- Kết hợp Rust rewrite, thay đổi ICU và identical code folding, kích thước binary Bun trên Linux và Windows giảm khoảng 20%
| Version | Platform | Size |
|---|---|---|
| Bun v1.4.0 canary | Windows | 76 MB |
| Bun v1.3.14 | Windows | 94 MB |
| Bun v1.4.0 canary | Linux | 70 MB |
| Bun v1.3.14 | Linux | 88 MB |
- TOML parser và các recursive-descent parser của Bun dùng ít stack space hơn
- LLVM IR codegen của Rust phát ra các intrinsic
llvm.lifetime.startvàllvm.lifetime.endcho stack variable, giúp LLVM tái sử dụng stack slot - Trước đây, để обход qua open issue của Zig, họ phải tự refactor các hàm đặc biệt lớn thành nhiều hàm nhỏ hơn
- Rust hỗ trợ cross-language link-time optimization giữa C/C++ và Rust, cho phép inlining giữa các ngôn ngữ
-
Benchmark Linux x64
- So sánh Bun v1.3.14 và Bun v1.4.0 trên Linux x64 EC2 Xeon Platinum 8488C
- HTTP throughput được đo bằng oha, app workload được đo bằng hyperfine
- | server | Bun v1.3.14 | Bun v1.4.0 | Δ |
- | --- | ---: | ---: | ---: |
- | Bun.serve | 169,6k req/s | 177,7k req/s | +4,8% |
- | node:http | 103,8k req/s | 108,5k req/s | +4,5% |
- | Elysia | 158,9k req/s | 163,3k req/s | +2,8% |
- | express | 64,5k req/s | 66,6k req/s | +3,2% |
- | fastify | 91,5k req/s | 95,9k req/s | +4,8% |
- | workload | Bun v1.3.14 | Bun v1.4.0 | Δ |
- | --- | ---: | ---: | ---: |
- | next build | 13,62 s | 13,03 s | +4,5% |
- | vite build | 1,69 s | 1,65 s | +2,2% |
- |
tsc -b --force| 0,94 s | 0,89 s | +4,7% |
Các trường hợp sử dụng thực tế và trạng thái phát hành
- Prisma đã ra mắt public beta của Prisma Compute trên nền bản rewrite Rust của Bun
- Phía Prisma cho biết họ đã kiểm thử trên bản rewrite Rust các failure mode gồm connection pool không khôi phục sau khi VM pause/resume và memory leak, và bản rewrite đã xử lý tốt các failure mode này
- Claude Code v2.1.181 và các phiên bản sau bản phát hành ngày 17 tháng 6 sử dụng Bun được port sang Rust
- Startup trên Linux của Claude Code nhanh hơn 10%, còn ngoài ra hầu hết người dùng gần như không nhận ra khác biệt
- Bun v1.3.14 là phiên bản Bun cuối cùng được viết bằng Zig
- Bun v1.4.0 là phiên bản Bun đầu tiên được viết bằng Rust và được cung cấp dưới dạng canary
Công cụ đội ngũ thu được và những việc còn lại
- Codebase Rust mới vẫn giữ hình thái rất giống codebase Zig hiện có
- Được viết để những người hiểu mã Zig ban đầu cũng có thể hiểu được mã Rust được dịch một cách cơ học
- Việc review PR rewrite Rust được tiến hành bằng cách kiểm tra xem agent review đối nghịch có phát hiện đúng các điểm không nhất quán giữa Zig và Rust, hướng dẫn porting, và việc tuân thủ lifetime guide hay không, đồng thời con người đọc nhiều đoạn mã theo kiểu side-by-side
- Bun v1.4 giúp Bun nhanh hơn, nhỏ hơn, giảm mức sử dụng bộ nhớ và cung cấp công cụ để cải thiện độ ổn định
- Rust borrow checker
- Miri
- LeakSanitizer
- coverage-guided fuzzing 24/7 cho parser
- Vẫn còn những phần cần refactor, và bun-unsafe-audit đã được kết nối
- Một kỹ sư đã theo dõi sát sao Fable và Claude Code, đạt tới trạng thái toàn bộ test suite pass trên mọi nền tảng chỉ trong 11 ngày
1 bình luận
Các ý kiến trên Lobste.rs
extern "C", nhưng vẫn sẽ phải dựa vào style guide được ép buộc qua code review, và dù có ASAN thì hỏng bộ nhớ và rò rỉ bộ nhớ vẫn sẽ tiếp diễnThú vị là Node.js vẫn chạy tốt với C++, nhưng tôi chưa bao giờ xem Bun là một dự án nghiêm túc. Giờ nó trông như một test bench của bộ phận marketing Anthropic, nên tôi vẫn sẽ tránh xa
Spoiler: thực tế thì người ta không cẩn thận đến thế, và vẫn mắc lỗi
unsafe, và 78% trong số đó chỉ là một dòng” nghe như muốn trấn an, nhưng việc một khốiunsafecó một dòng hay không không quan trọng. Nếu trong đó phá vỡ các bảo đảm an toàn, thì toàn bộ mã bên ngoài khối cũng có khả năng bị phá vỡ soundnessBản merge ban đầu của port Rust cho Bun có chứa dạng unsoundness rõ ràng như thế này: https://github.com/oven-sh/bun/issues/30719
Vấn đề đó đã được các maintainer phản hồi bằng cách bật công cụ Miri của Rust trong CI, và phần “What's Next” của bài viết cũng có Miri (which runs for a growing chunk of code in CI), nên có vẻ họ đang làm theo hướng đó, điều này là tốt
Công bằng mà nói, ngay cả Rust có vi phạm an toàn cũng có thể dễ bảo trì hơn tùy vào chất lượng của mã Zig mà nó thay thế. Dù vậy, số dòng mã trên mỗi khối unsafe không phải là thước đo chất lượng, đặc biệt nếu các khối đó cũng không đi kèm thực hành lập trình, chuyên môn hay kiểm tra tự động khác
unsafekhông phát sinh từ quá trình port, mà xuất phát từ yêu cầu của dự án. Nếu gọi thư viện C thì cần khốiunsafe, và không có cách nào loại bỏ chỉ bằng refactoringTất nhiên nếu viết lại cả thư viện C đó thì có thể, nhưng chuyện đó có thể xem xét sau
Trong thông báo “Bun is joining Anthropic”, Jarred nói họ sẽ tuyển thêm kỹ sư để làm Bun, nhưng nhìn trên GitHub thì đội Bun có vẻ lại còn nhỏ đi. Tôi không rõ rút ra kết luận gì từ đây, chỉ là “Bun là một nhóm nhỏ” mà thôi
Bản thân phương pháp thì thú vị, nhưng bài viết đọc như một bài marketing. Nó cũng thiếu phân tích về chi phí, không nói đến rủi ro đi kèm việc viết lại bằng Rust, và ngay từ đầu cũng giải thích khá mơ hồ vì sao việc viết lại xảy ra. Nếu đoán thì có thể là do chính sách không AI của Zig, hoặc do nội bộ Anthropic có định hướng tập trung vào Rust
Còn về lý do viết lại, tôi thấy bài viết kể một câu chuyện khá nhất quán. Bun vẫn tiếp tục crash dù đã có các biện pháp để bắt lỗi, và các developer muốn một cách có hệ thống hơn để ngăn các vấn đề như vậy
Kế hoạch ban đầu là ép một số phong cách lập trình chặt chẽ hơn và đưa smart pointer vào, nhưng Jared cho rằng smart pointer tự làm có usability kém hơn Rust và cũng không có bảo đảm. Vì vậy nó trở thành “hay thử trong một tuần xem model mới của Anthropic có thể viết lại Bun bằng Rust không?”, và khi tỷ lệ vượt qua test suite tăng lên, luồng diễn biến có vẻ đã chuyển từ “đáng thử” sang “sẽ merge”
Nói cách khác, có vẻ không phải là quyết định viết lại bằng Rust ngay từ đầu, mà gần hơn với “Rust có vẻ đưa ra lời giải cho vấn đề, nhưng không làm được vì chi phí viết lại. Nhưng thử port bằng LLM thì thấy có khả năng. Vậy hãy viết lại bằng LLM”
Xin chúc mừng Jarred, đội Bun và Anthropic vì đã làm được việc này