18 điểm bởi xguru 2024-11-04 | 12 bình luận | Chia sẻ qua WhatsApp

Những điều làm tốt

  • Việc viết lại được thực hiện theo các bước nhỏ (dần dần, stop-and-go), hoạt động tốt, và mã mới trở nên dễ đọc, dễ hiểu hơn
  • Có được cái nhìn bao quát toàn bộ mã nguồn nên tìm ra các cơ hội tối ưu hiệu năng
  • Loại bỏ được khoảng 1/3 ~ 1/2 lượng mã không còn được sử dụng. Các ngôn ngữ lập trình hiện đại như Rust hay Go phát hiện dead code tốt hơn và báo cho lập trình viên biết
  • Không phải lo truy cập ngoài phạm vi hay overflow/underflow
  • Framework kiểm thử tích hợp sẵn rất hữu ích
  • Rất vui vì có thể bỏ các file CMake

Những điều không suôn sẻ

Vẫn phải lần theo undefined behavior

  • Khi viết lại dần dần từ C/C++ sang Rust, đã phải dùng rất nhiều raw pointer và các khối unsafe{}
  • Các quy tắc của Rust vẫn áp dụng bên trong unsafe, nhưng vì compiler không kiểm tra nên undefined behavior rất dễ xảy ra
  • Trong unsafe, rất dễ phá vỡ quy tắc nhiều con trỏ chỉ đọc XOR một con trỏ có thể thay đổi
  • Miri đóng vai trò cứu tinh khi bắt được những lỗi này

Miri không phải lúc nào cũng hoạt động và vẫn phải dùng Valgrind

  • Nếu dùng thư viện có những phần được viết bằng C hoặc assembly, như thư viện mã hóa, thì Miri sẽ không hoạt động
  • Có nhiều mã unsafe mà Miri không kiểm tra được
  • Một số bài kiểm thử phải chạy bằng valgrind

Vẫn phải theo dõi memory leak

  • Mẫu phổ biến của C API là cấp phát bộ nhớ trong MYLIB_init() và giải phóng trong MYLIB_release(), nhưng rất dễ quên gọi MYLIB_release
  • Lập trình viên Rust muốn tạo wrapper object bằng RAII, nhưng trong các bài kiểm thử dùng C API thì không thể dùng tính năng này
  • Với logic phức tạp, rất khó luôn luôn gọi hàm dọn dẹp. Trong C có thể giải quyết bằng goto, nhưng Rust không hỗ trợ
  • Đã giải quyết bằng crate defer, nhưng borrow checker không thích điều đó

Cross-compilation không phải lúc nào cũng chạy được

  • Giống như Miri, nếu dùng thư viện có phần được triển khai bằng C hoặc assembly thì cargo build --target=... sẽ không hoạt động ngay

Cbindgen không phải lúc nào cũng hoạt động

  • Cbindgen được dùng nhiều để tạo C header từ codebase Rust, nhưng có những giới hạn hoặc bug

ABI không ổn định

  • Những kiểu hữu ích của standard library như Option không có ABI ổn định, nên phải sao chép thủ công bằng annotation repr(C)

Thiếu hỗ trợ cho custom memory allocator

  • Nhiều thư viện C cho phép người dùng cung cấp allocator ở runtime. Trong Rust chỉ có thể chọn global allocator tại compile time
  • Vấn đề dọn dẹp tài nguyên có thể được giải quyết bằng arena allocator, nhưng trong Rust điều này không mang tính idiomatic và không tích hợp với standard library

Độ phức tạp

  • Phải dùng những thứ như UnsafeCell, RefCell, MaybeUninit, Pin để xử lý FFI nên độ phức tạp tăng cao
  • Rust thuần đã phức tạp sẵn, mà thêm cả lớp FFI vào nữa thì trở thành một con quái vật
  • Thậm chí có những lập trình viên đã từ chối làm việc trên codebase này vì độ phức tạp của Rust

Kết luận

  • Nhìn chung hài lòng với việc viết lại bằng Rust, nhưng cũng thất vọng ở một số mảng, và cần nhiều công sức hơn rất nhiều so với dự đoán
  • Rust tương tác nhiều với C cho cảm giác như một ngôn ngữ hoàn toàn khác so với khi dùng Rust thuần. Có rất nhiều ma sát và cạm bẫy. Nhiều vấn đề của C++ mà Rust tuyên bố đã giải quyết thì thực ra hoàn toàn chưa được giải quyết
  • Xin cảm ơn sâu sắc tới những người phát triển Rust, Miri, cbindgen, v.v. Họ đã làm nên một công việc đáng kinh ngạc. Dù vậy, ngôn ngữ và công cụ cho trường hợp dùng nhiều C FFI vẫn còn non nớt, gần như cảm giác trước cả v1.0
  • Nếu ergonomics của unsafe, standard library, tài liệu, công cụ, và ABI không ổn định được cải thiện trong tương lai, trải nghiệm có thể sẽ dễ chịu hơn nhiều
  • Có vẻ Microsoft và Google cũng cảm nhận được tất cả những điều này nên đang thực sự đầu tư tiền vào lĩnh vực này
  • Nếu vẫn chưa biết Rust, dự án đầu tiên nên dùng Rust thuần và tránh xa chủ đề FFI
  • Ban đầu tác giả từng cân nhắc dùng Zig hoặc Odin cho lần viết lại này, nhưng không muốn dùng một ngôn ngữ chưa tới v1.0 cho codebase production của doanh nghiệp. Giờ thì lại tự hỏi liệu trải nghiệm đó có thực sự tệ hơn Rust hay không. Có lẽ mô hình Rust thực sự không hợp với mô hình C (hoặc C++), nên khi dùng cả hai cùng nhau thì ma sát quá lớn
  • Nếu sau này phải làm một công việc tương tự, tác giả sẽ cân nhắc Zig một cách nghiêm túc. Mỗi khi ai đó nói “cứ viết lại bằng Rust đi”, hãy đưa cho họ bài viết này và hỏi xem họ đã đổi ý chưa

12 bình luận

 
bus710 2024-11-05

Dù Zig vẫn đang ở giai đoạn pre-v1, nó vẫn dùng được khá nhiều thư viện C nên thực tế hữu dụng hơn tưởng tượng. Khi cần chồng thêm thứ gì đó lên một dự án đang chạy dựa trên C, Zig có thể phù hợp hơn Rust.

 
ahwjdekf 2024-11-04

khi tìm hiểu Rust, ngay khoảnh khắc nhìn thấy từ khóa unsafe, tôi đã có cảm giác rợn người...

 
jkliop890 2024-11-04

Tôi không nghĩ Rust có thể giải quyết những vấn đề cố hữu mà C++ đang có. Đây là góc nhìn thực tế hơn là góc nhìn về cú pháp.

Lý do là:

  1. Đã có quá nhiều hệ thống production đang dùng C/C++. Và chúng đang vận hành ổn định. Hơn nữa, phần lớn trong số đó cũng không nhất thiết muốn port sang Rust.

  2. Ngay từ đầu, phần cứng không được tạo ra với giả định về reference counting. Nhiều trường hợp dùng C/C++ là để điều khiển phần cứng, OS, driver, tầng binary ở tốc độ cao; nhưng để hỗ trợ Rust thì lập trình viên low-level cuối cùng vẫn phải trực tiếp quản lý vòng đời tài nguyên bằng unsafe, và đây cũng là một chi phí lớn.

Tôi cho rằng trải nghiệm của tác giả quan trọng hơn giá trị tiềm ẩn của ngôn ngữ hay những câu chuyện mang tính lý thuyết.
Tôi cảm thấy mức độ quản lý tài nguyên trong những lĩnh vực thực sự cần ngôn ngữ cấp C/C++ giống như một miếng sườn gà khi muốn thay thế bằng Rust.

 
cosine20 2024-11-04

Bài này cũng lao vào Rust với một sự hiểu sai về nó.
Nhìn nội dung thì có vẻ đây là một thư viện phải thường xuyên giao tiếp với bên ngoài Rust, mà đến thời điểm đó thì gần như chắc chắn sẽ trở nên lộn xộn... Ngay từ đầu đã chẳng có ngôn ngữ native nào là không lộn xộn, còn Rust thì vì ở cấp độ ngôn ngữ nó bọc phần đó lại một cách an toàn, nên càng có nhiều điểm tiếp xúc với bên ngoài ngôn ngữ thì lợi thế đó càng giảm đi nhiều.

Nhiều vấn đề của C++ mà Rust tuyên bố đã giải quyết thực ra hoàn toàn không được giải quyết

Điều này đúng ở một mức độ nào đó, nhưng trong môi trường phát triển của bài gốc thì vốn dĩ không thể giải quyết được, nên tôi nghĩ vấn đề là ở chỗ họ đã tiếp cận Rust như thể nó là thuốc chữa bách bệnh.

 
kotlinc 2024-11-04

Tôi cảm thấy ý của bài là vì trong quá trình chuyển đổi dần từ C/C++ sang Rust thì buộc phải dùng unsafe, nên việc chuyển sang Rust không có nhiều ý nghĩa. Thay vì chuyển đổi dần sang Rust, tôi sẽ chọn Zig. Nhưng trong bài gốc có chỗ nào nói rằng đó là một thư viện phải thường xuyên giao tiếp với bên ngoài Rust không?

 
cosine20 2024-11-04

Việc dùng FFI đồng nghĩa với việc giao tiếp với bên ngoài Rust.
Và nhìn vào nội dung bài thì có vẻ không chỉ dừng ở mức trao đổi một vài trạng thái hay dữ liệu đơn giản, mà là bên trong và bên ngoài tương tác với nhau một cách phức tạp.

 
kotlinc 2024-11-05

Nếu muốn dần dần chuyển một thư viện viết bằng C sang Rust thì chẳng phải FFI là điều không thể tránh khỏi sao? Có lẽ sẽ phải thay từng phần nhỏ của chương trình bằng Rust và xử lý phần C còn lại bằng FFI, nên có phải ý bạn khi nói "giao tiếp với bên ngoài" là những công việc như vậy không? Nếu vậy thì tôi nghĩ việc tác giả bài gốc trở nên hoài nghi về Rust cũng là điều tự nhiên. Trừ khi thay toàn bộ mã cùng một lúc, nếu không sẽ không có được lợi ích của Rust, nên mới khuyến nghị Zig chăng.

 
cosine20 2024-11-05

^-^

 
savvykang 2024-11-04

Vì các phần unsafe được đánh dấu tường minh trong mã nguồn, nên tôi kỳ vọng sẽ hữu ích ở chỗ có thể xác định toàn bộ phạm vi ảnh hưởng của FFI, miễn là không dùng khối unsafe ngay từ điểm vào của chương trình. Nhưng có vẻ điều này không để lại nhiều ấn tượng với tác giả.

 
carnoxen 2024-11-04

Ngay từ thời điểm dùng FFI, có thể xem như thiết kế an toàn đã không còn khả thi nữa.

 
cosine20 2024-11-04

Đúng vậy.

 
kohs100 2024-11-04

Đúng vậy, đã mạnh dạn viết rằng nó bị trát đầy unsafe mà cuối cùng vẫn nói là chưa được giải quyết sao...