2 điểm bởi GN⁺ 2024-07-16 | 1 bình luận | Chia sẻ qua WhatsApp
  • Tại LSFMM+BPF Summit 2024, đã có thảo luận về cách áp dụng Rust cho hệ thống tệp Linux, và bản vá RFC thứ hai xuất hiện sau RFC tháng 12/2023 trở thành tâm điểm tranh luận
  • Phía Rust-for-Linux muốn đưa các yêu cầu của API hệ thống tệp vào hệ thống kiểu của Rust để phát hiện lỗi ở thời điểm biên dịch, tự động hóa việc dọn dẹp tài nguyên và giảm các lỗ hổng liên quan đến bộ nhớ
  • Trường hợp iget_locked() cho thấy hướng đi mà get_or_create_inode() trong Rust muốn ép buộc bằng kiểu và cơ chế dọn dẹp tự động đối với các bước như kiểm tra null, phân biệt trạng thái inode và xử lý lỗi vốn trước đây do phía gọi trong C tự thực hiện
  • Dave Chinner, Christian Brauner, James Bottomley, Ted Ts'o và những người khác lo ngại về sự không khớp tên gọi giữa API C và API Rust, việc đồng bộ API, khác biệt trong vòng đời đối tượng và gánh nặng bảo trì khi phải tính đến hơn 50 hệ thống tệp
  • Cốt lõi của xung đột không nằm ở bản thân ưu điểm của lớp trừu tượng Rust, mà ở câu hỏi ai sẽ gánh nỗi đau khi phải giữ cho binding và lớp trừu tượng Rust theo kịp lúc mã C tiếp tục thay đổi

Phiên thảo luận Rust cho hệ thống tệp tại LSFMM+BPF

  • Tại Linux Storage, Filesystem, Memory Management, and BPF Summit 2024, Wedson Almeida Filho và Kent Overstreet đã bàn về cách sử dụng Rust cho hệ thống tệp Linux
  • Almeida đã gửi bộ bản vá RFC về lớp trừu tượng Rust cho hệ thống tệp vào tháng 12/2023, và đã có khác biệt quan điểm xoay quanh cách tiếp cận này
  • Cùng ngày diễn ra phiên họp vào giữa tháng 5, Almeida đã gửi phiên bản RFC thứ hai để thảo luận cùng các chủ đề Rust khác

Mục tiêu của lớp trừu tượng hệ thống tệp trong Rust-for-Linux

  • Lớp trừu tượng hệ thống tệp được đề xuất phản ánh định hướng mà Rust-for-Linux project theo đuổi
  • Trọng tâm là biểu đạt nhiều hơn các yêu cầu của API hệ thống tệp bằng hệ thống kiểu của Rust để bắt lỗi ngay ở thời điểm biên dịch
  • Đồng thời muốn tự động hóa những việc khó cung cấp một cách an toàn trong mã C
    • Ví dụ: dọn dẹp tài nguyên
  • Mục tiêu là làm cho trải nghiệm phát triển hệ thống tệp hiệu quả hơn, giảm thời gian debug các vấn đề mà compiler có thể phát hiện, và hạ thấp các lỗ hổng liên quan đến bộ nhớ
  • Overstreet cho biết ông đã quá nhiều lần phải truy vết các bug kéo dài 2 tuần trong bcachefs và tin rằng Rust mang lại nhiều thứ hơn C
    • Rust loại bỏ hành vi không xác định
    • Cung cấp khả năng nhìn thấy những gì đang diễn ra bên trong mã
    • Nếu có thể chứng minh tính đúng đắn của mã Rust, số bug cản trở phát triển tính năng sẽ giảm đi rất nhiều

Ví dụ về hệ thống kiểu quanh iget_locked()

  • Almeida lấy iget_locked() trong kernel hiện tại làm ví dụ trên slide để minh họa các yêu cầu phức tạp
  • Phía gọi trong C phải tự xử lý nhiều điều kiện
    • Phải kiểm tra xem giá trị trả về có phải null hay không
    • Phải xác định struct inode trả về là inode mới hay inode đã tồn tại
    • Nếu là inode mới thì phải khởi tạo trước khi dùng
    • Nếu khởi tạo thất bại thì phải gọi iget_failed()
  • Al Viro không đồng ý với một phần các yêu cầu mà Almeida nêu cho phía gọi iget_locked(), và tranh luận tiếp tục xoay quanh hành vi chi tiết
  • Overstreet cho rằng nếu đóng gói các quy tắc này vào kiểu và lớp trừu tượng Rust, compiler có thể ép buộc cách xử lý đúng
  • Hàm tương ứng phía Rust mà Almeida đưa ra là get_or_create_inode()
    • Cũng như trong C, vẫn phải kiểm tra việc thất bại
    • Khi thành công, phía gọi sẽ nhận một inode đếm tham chiếu thông thường hoặc một inode mới
    • Với inode thông thường, bộ đếm tham chiếu sẽ tự giảm khi đối tượng không còn được tham chiếu nữa
    • Với inode mới, nếu không được khởi tạo thì sẽ tự động gọi xử lý tương đương iget_failed()
    • Khi inode mới đã được khởi tạo một lần, nó trở thành inode thông thường và sau đó áp dụng cơ chế tự giảm đếm tham chiếu
    • Các hành vi này được ép buộc bằng hệ thống kiểu
  • Viro đặt câu hỏi các ràng buộc này thực sự sẽ được định nghĩa ở đâu trong mã nguồn
  • Almeida trả lời rằng ông muốn xác định các ràng buộc từ Viro và các nhà phát triển hệ thống tệp khác, rồi tạo ra các kiểu và lớp trừu tượng để ép buộc chúng

Sự đứt gãy giữa API C và API Rust

  • Dave Chinner cho rằng nếu API C và API Rust có tên khác nhau thì nhà phát triển hiện có sẽ khó nhìn mã C để biết lời gọi tương ứng trong Rust là gì
  • Cũng có lo ngại rằng nếu không dùng cùng tên, nó có thể trở thành một API hoàn toàn xa lạ với cộng đồng phát triển hiện tại
  • Khi mã C thay đổi thì mã Rust cũng phải chạy theo, nên câu hỏi ai sẽ đảm nhận công việc đó vẫn còn bỏ ngỏ
  • Almeida thừa nhận đây là vấn đề cần thảo luận
    • Ông không phản đối việc đổi tên
    • Nhưng ông cũng không cho rằng iget_locked() là một cái tên tốt, và xem đây có thể là cơ hội để đặt tên tốt hơn
  • Viro cho rằng iget_locked() không phải là hàm thành viên của đối tượng superblock mà là một hàm thư viện, nên ví dụ này không phù hợp
  • Almeida trả lời rằng get_or_create_inode() cũng có thể chuyển thành hàm thư viện, và ví dụ này nhằm cho thấy cách mã hóa các ràng buộc vào kiểu

Lựa chọn giữa lớp trừu tượng tổng quát và cách tiếp cận xoay quanh hệ thống tệp đơn giản

  • Christian Brauner cho rằng trước tiên cần xác định liệu lớp trừu tượng Rust có phải là lớp trừu tượng tổng quát cho mọi hệ thống tệp trong kernel hay chỉ tập trung vào những khả năng cần thiết cho các hệ thống tệp đơn giản hơn được viết bằng Rust
  • Về lâu dài, nếu một hàm như get_or_create_inode() chứa nhiều ràng buộc hơn rất nhiều so với iget_locked(), vấn đề có thể phát sinh
  • Mã C, đặc biệt là ở giai đoạn đầu, có thể tiến hóa nhanh hơn mã Rust, nên hai API sẽ phải liên tục được đồng bộ
  • Overstreet cho rằng điểm mấu chốt là có thực hiện refactor và dọn dẹp cùng lúc khi thêm lớp trừu tượng Rust hay không, và ông rất tin rằng điều đó là cần thiết
  • James Bottomley cho rằng vòng đời đối tượng được mã hóa trong API Rust nhưng phía C lại không có biểu đạt tương ứng
    • Nếu vòng đời đối tượng thay đổi ở một bên, bug có thể xuất hiện ở bên kia
  • Chinner nói rằng vòng đời của đối tượng inode đôi khi khác nhau tùy từng hệ thống tệp
    • Nếu đưa một cách hiểu vòng đời duy nhất vào API, các hàm đó có thể không hoạt động với một số hệ thống tệp
  • Almeida trả lời rằng ví dụ này chỉ được dùng cho những hệ thống tệp hiện đang gọi iget_locked() và có thể hưởng lợi từ nó
    • Các nhà phát triển Rust không định ép hệ thống tệp phải thay đổi cách chúng đang hoạt động

“Ai sẽ gánh nỗi đau?”

  • Ted Ts'o nói rằng có vẻ như đang có nỗ lực chuyển tất cả mọi người sang “tôn giáo” Rust, nhưng Linux có hơn 50 hệ thống tệp và sẽ không thể chuyển đổi ngay lập tức
  • Mã C sẽ tiếp tục được cải thiện, và nếu các thay đổi đó làm vỡ binding Rust thì các hệ thống tệp phụ thuộc vào binding đó cũng có thể hỏng theo
  • Ts'o cho rằng trong một thời gian, binding Rust sẽ là công dân hạng hai, và binding Rust bị hỏng là vấn đề của các nhà phát triển Rust-for-Linux chứ không phải của toàn bộ cộng đồng hệ thống tệp
  • Ông cho rằng trong vòng 1–2 năm tới sẽ lộ rõ việc vừa phát triển binding Rust vừa để mã C tiến hóa, đồng thời nhồi nhiều ngữ nghĩa vào hệ thống kiểu, là cách tiếp cận tốt hay xấu
  • Với Ts'o, thay đổi lớn rốt cuộc là vấn đề phân bổ nỗi đau
    • Nhà phát triển thay đổi API C có thể nói rằng họ sẽ sửa phần mã C bị ảnh hưởng, nhưng không sửa binding Rust vì họ không biết Rust
  • Almeida trả lời rằng ông không cố định API C, mà muốn các nhà phát triển hệ thống tệp giải thích ngữ nghĩa của API để ông mã hóa chúng vào Rust
  • Bottomley cho rằng càng mã hóa nhiều ngữ nghĩa vào binding thì nó càng dễ tổn thương hơn từ góc nhìn đồng bộ
  • Almeida đáp rằng khi API thay đổi thì vẫn phải cập nhật người dùng API, cũng như với mọi người dùng khác

Nên đưa điều gì vào method, function và type

  • Viro lại đặt vấn đề với việc phương án thay thế iget_locked() phụ thuộc vào method
    • Ông cho rằng khi dùng method thì tham số không được chỉ định một cách tường minh
  • Overstreet cho rằng sự khó chịu với method bắt nguồn từ các ngôn ngữ như C++ vốn phụ thuộc quá nhiều vào kế thừa
    • Rust không làm như vậy, và method trong Rust phần lớn là yếu tố cú pháp
  • Jan Kara phân biệt giữa hành vi gắn với chính inode và hành vi nội tại của hàm iget_locked()
    • inode đi kèm những hành vi như đếm tham chiếu và cách xử lý của nó
    • hàm iget_locked() có những hành vi riêng của nó
  • Overstreet và Almeida trả lời rằng cả hai phần đều được mã hóa vào kiểu nhưng tách biệt, và các hàm khác sử dụng kiểu inode có thể có giá trị trả về với thuộc tính khác nhau
  • Viro giải thích vì sao inode trong VFS hiện hoạt động theo cách hiện nay, và đồng ý rằng nên bắt đầu nhỏ rồi xem hướng đi tiếp theo
  • Overstreet nói ví dụ lần này có thể quá phức tạp nên không phải điểm khởi đầu tốt, còn Viro đáp “Không, không phải vậy”, rồi phiên thảo luận kết thúc

1 bình luận

 
GN⁺ 2024-07-16
Ý kiến trên Hacker News
  • Tôi không hiểu việc nói rằng mỗi hệ thống tệp có vòng đời inode tùy chỉnh, nhưng lại dùng cùng các hàm quản lý vòng đời và chỉ khác về ngữ nghĩa
    Nếu cùng một hàm phải được dùng khác nhau tùy theo chi tiết triển khai, nghe có vẻ như trái ngược với một lớp trừu tượng
    Nếu vòng đời inode là theo từng hệ thống tệp, thì nên quản lý bằng các hàm riêng cho từng hệ thống tệp

    • Tôi cũng có cùng thắc mắc, và có vẻ họ đang cố hiểu hoặc tài liệu hóa toàn bộ C API để phục vụ công việc với Rust
      Khi tập hợp các thông tin như vậy, có thể sẽ lộ ra những điểm cần refactor để ngay từ đầu những câu hỏi kiểu này không phát sinh, và đó là điều tốt
    • Nếu đây là tài liệu bạn mới thấy lần đầu, https://www.kernel.org/doc/html/latest/filesystems/vfs.html có thể hữu ích
      Đây là phần tổng quan về lớp VFS, xử lý hành vi theo từng hệ thống tệp nhưng vẫn duy trì một giao diện nhất quán ở phía kernel
    • Tôi hiểu là họ muốn trừu tượng hóa tối đa những phần có thể áp dụng phổ quát trong lớp VFS, còn các ngoại lệ không khớp thì xử lý ở lớp theo từng hệ thống tệp
      Vòng đời inode có thể chỉ là một ví dụ ban đầu để mở đầu cuộc thảo luận
    • Có vẻ họ muốn làm cho compiler hoạt động theo cách theo dõi thời gian sống của inode
      Compiler sẽ hỗ trợ các tham chiếu tạm thời, nhưng hệ thống tệp vẫn phải lưu số lượng liên kết trên đĩa
    • Có nhiều hàm có thể dùng để tạo inode và đưa vào cache, trong đó iget_locked() được tập trung ở đây là một mẫu cụ thể
      Không phải hệ thống tệp nào cũng dùng cách đó, và tùy tình huống cũng có thể không dùng
      Ví dụ FAT không dùng vì nó tự tạo số inode và duy trì ánh xạ riêng từ vị trí FAT sang inode
      Cũng có những hệ thống tệp như proc không cache đối tượng inode
      Bản thân đối tượng inode dường như có cùng luồng trạng thái bất kể nó đến từ đâu, nên từ góc nhìn của bên tiêu thụ, cách dùng inode không thay đổi
      Điều khác nhau là cách lớp hệ thống tệp tạo đối tượng inode và xử lý nội bộ nó
  • Có lẽ đang đặt sai câu hỏi
    Rust có cần thay đổi để gọi C dễ hơn không?
    Tôi đã thử Rust một chút, nhưng từ góc nhìn của một lập trình viên nghiệp dư, tôi vẫn chưa rõ phải tương tác với C như thế nào
    Trong khi đó với C++ hay Objective C thì chỉ cần include đúng header và gọi hàm
    Swift có thể include file Objective C, rồi từ đó gọi C
    Trong trường hợp này, thay vì kỳ vọng các nhà phát triển kernel phải thích nghi với ngôn ngữ, có lẽ ngôn ngữ Rust nên linh hoạt hơn một chút

    • Việc gọi C trong Rust khá đơn giản
      Chỉ cần khai báo hàm bên ngoài rồi gọi
      Ví dụ như trong sách Rust tại https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html#usin..., có thể khai báo bằng extern "C"
      Nếu không muốn viết tay toàn bộ khai báo cho một thư viện phức tạp, có thể dùng công cụ như bindgen để tự động tạo các khai báo extern từ file header C: https://github.com/rust-lang/rust-bindgen
      Có thể lập luận rằng sẽ tốt hơn nếu thứ như bindgen được tích hợp vào Rust để dùng mà không cần phụ thuộc bên thứ ba hay cấu hình build.rs, nhưng đó không phải trọng tâm của bài viết này
      Vấn đề không phải là binding cấp thấp, mà là wrapper cấp cao theo phong cách Rust; và không thể có một công cụ tổng quát nào tự động tạo ra những wrapper như vậy từ mã C tùy ý
    • Đây không phải là điểm khó quan trọng trong Rust, cũng không liên quan đến chủ đề của bài
      Bài viết nói về việc tìm cách thực sự triển khai driver filesystem trong kernel bằng Rust
      Một điểm quan trọng nữa là mã Rust trong kernel tất yếu sẽ tiêu thụ các interface C
      Với các use case đang nghĩ tới, bindgen khá phù hợp: https://github.com/rust-lang/rust-bindgen
    • Thực ra khá dễ
      Muốn gọi từ Rust thì khai báo extern "C" fn foo() -> T, rồi truyền cờ liên kết bằng thuộc tính #[link] hoặc build.rs
      Có thể tạo sẵn binding bằng crate bindgen, hoặc tạo trong build.rs rồi include bằng include!()
      Thông thường sẽ tạo một crate -sys chỉ chứa các binding được sinh ra, còn mã thực tế thì use binding từ crate sys đó như bình thường
      Ngay cả trong C++ và Objective C, không chỉ include đúng header mà còn phải link tới thư viện
    • Điểm cốt lõi là Rust có thể mô hình hóa các bất biến mà C không thể biểu đạt
      Gọi hai chiều là có thể, nhưng nếu C không biểu đạt được những gì Rust có thể biểu đạt, điều đó sẽ ảnh hưởng lớn đến thiết kế API mà cả hai bên phải dùng chung
    • Việc gọi C vốn đã rất đơn giản nên Rust không cần thay đổi để dễ hơn nữa
      Chỉ cần khai báo hàm C bằng extern "C" rồi gọi
      Thường cần unsafe và phải chuyển tham chiếu sang con trỏ thô hoặc ép kiểu, nhưng bản thân cú pháp thì đơn giản
      Cũng có công cụ quét file header C để tạo khai báo, trong đó bindgen được dùng nhiều nhất
      Điểm tranh luận của bài này gần với việc dùng Rust như thế nào hơn là bản thân ngôn ngữ
      Các nhà phát triển Rust-for-Linux muốn dùng tính năng và hệ thống kiểu của Rust để mã hóa ngữ nghĩa của các lời gọi API, khiến chúng an toàn hơn và ít lỗi hơn
      Phía những người làm C lo rằng làm vậy sẽ khiến hành vi và ngữ nghĩa của C API khó phát triển hơn
      Vì khi C API thay đổi thì Rust API cũng phải được sửa, và họ không muốn gánh phần việc đó
      Một phương án dễ chấp nhận hơn là dùng ít tính năng và hệ thống kiểu của Rust hơn khi mã hóa ngữ nghĩa vào Rust API
      Như vậy, khi C API thay đổi, việc cập nhật Rust API sẽ mang tính cơ học và đơn giản; nhưng nếu Rust-for-Linux không thể dùng các tính năng của Rust để tạo API tốt hơn và an toàn hơn, thì sẽ nảy sinh câu hỏi ý nghĩa của công việc này là gì
      Dù vậy, cũng hơi lạ khi thừa nhận mình chưa hiểu đủ về ngôn ngữ mà lại nói chắc chắn về chủ đề này
  • Vì không rành filesystem của Linux nên tôi không rõ Rust API này là wrapper quanh C API, hay là một bản triển khai lại
    Nếu là triển khai lại hoặc là API riêng, việc giữ nguyên tên như C API có vẻ sẽ càng ngày càng gây nhầm lẫn
    Tôi nghĩ vậy dù ban đầu nó có thể giúp các lập trình viên quen thuộc hiểu nhanh hơn

    • Almeida đã cho thấy thứ tương đương với iget_locked() trong Rust, và tên của nó là get_or_create_inode()
      Câu trả lời có vẻ là triển khai lại, và không dùng cùng tên
  • Xét cách những cuộc thảo luận kiểu này thường diễn ra và quy mô thay đổi liên quan, cuộc thảo luận lần này lịch sự một cách đáng ngạc nhiên
    Tôi không đồng ý với bầu không khí tiêu cực trong thread này
    Tôi khá lạc quan ở chỗ những người liên quan đã truyền đạt rõ các điểm đau cốt lõi mà không nói nhảm

    • Tôi đọc tiếp còn vì phần ghi chép tổng hợp xuất sắc hơn là vì nội dung
      Cuộc thảo luận thực tế hẳn đã gay gắt, lan man và nhiều bắt bẻ, đúng kiểu tranh luận ngôn ngữ lập trình giữa những người lập dị có quan điểm mạnh
      Có vẻ Jake Edge, người viết bản tóm tắt này, rất giỏi trong việc gạt bỏ những phần đó và chỉ ghi lại phần cốt lõi
  • Một số bình luận bên dưới trang lwn.net khá thô lỗ
    Hãy thử tưởng tượng bạn nhận được bình luận kiểu Khoa học tiến bộ sau mỗi đám tang trong một dự án mã nguồn mở mà bạn đang đóng góp

  • Việc nhân Linux có thêm nhiều lựa chọn luôn là điều có lợi
    Nhưng Rust có thể không phải là câu trả lời cho mọi thứ
    Rust cố gắng hết sức để bảo đảm một mô hình lập trình an toàn, nhưng mô hình đó cũng có giới hạn
    Có thể trông như cứ vấn đề bộ nhớ thì dùng Rust, vấn đề đồng thời thì chuyển sang Rust, nhưng không thể làm mọi việc mà C làm nếu không có các khối unsafe
    Rust có thể đem lại góc nhìn mới cho những vấn đề này, nhưng không phải là giải pháp hoàn chỉnh

    • Ưu điểm lớn của Rust trong công việc này là nó tích cực đóng gói các vấn đề an toàn đó bằng kiểu dữ liệu, và bài viết này cũng nói đúng về điều đó
      C, đặc biệt là C dùng trong kernel, đẩy trách nhiệm cho từng người phải nắm hoàn toàn mọi quy tắc ngầm
      Điều đó không thể mở rộng
      Ngay cả các nhà phát triển kernel dùng cùng một cấu trúc dữ liệu, khi ở chung một phòng, cũng không thể hoàn toàn thống nhất về các quy tắc đó
      Rust mạnh ở chỗ làm lộ rõ các quy tắc cần biết, và nếu người khác có thể bảo đảm việc tuân thủ quy tắc thì biến nó thành chuyện không còn là vấn đề của mình nữa
      Đôi khi kết quả có thể kém tối ưu hơn, nhưng ngay cả trong kernel Linux, mặc định kém tối ưu hơn thường vẫn là đúng; còn với những ai có dư sức học thêm sáu quy tắc kỳ quặc để đạt hiệu năng tốt hơn thì chỉ cần cung cấp lối thoát unsafe
    • Có thể dùng các khối unsafe
      Chỉ là chỉ nên dùng khi cần thiết
      Việc dùng một khối unsafe có phạm vi ảnh hưởng rất hạn chế không có nghĩa là mọi bảo đảm nhận được ở phần mã còn lại đều biến mất
    • Đúng là các tác vụ cấp thấp cần mã unsafe
      Nhưng cho rằng vì phải dùng unsafe nên Rust không phù hợp là một hiểu lầm
      Mục đích của phân tách an toàn/không an toàn trong Rust là đánh dấu rõ phần nào của mã là không an toàn, để có thể tập trung kiểm toán vào phần nhỏ đó và nếu phần đó đúng thì có thể tin rằng phần còn lại sẽ hoạt động
    • Tò mò không biết trong số này có bao nhiêu phần thực sự cần thiết một cách 100% rõ ràng
      Có lý do chính đáng nào khiến mã hệ thống tệp bắt buộc phải là unsafe không?
      Có lẽ chỉ là một tập con rất nhỏ cần ở vài chỗ
    • Tôi thích Rust vì có những lúc cách hiểu của nó khớp rất tốt, nhưng cảm thấy phần async vẫn còn nhiều góc cạnh thô ráp
      Những gì xảy ra bên trong không trực quan
  • Nhìn vào biên bản họp, Rust trong kernel trông giống như chi phí phức tạp bổ sung
    Nếu viết lại hệ điều hành từ đầu thì có thể tận dụng trọn vẹn sức mạnh của ngôn ngữ
    Nhưng nếu gắn thêm bên cạnh một codebase khổng lồ đã tồn tại, nó sẽ phát sinh thêm vấn đề như thấy ở đây

    • Đúng, nhưng chi phí đó phải được bù lại bằng việc phát triển driver dễ hơn
      Hãy xem blog nói rằng driver GPU bằng Rust của Asahi Linux được làm trong một tháng
      Có thể tìm tales of the m1 gpu trên Google, và tác giả có quan điểm rất tiêu cực về Hacker News
      Nếu muốn thì có thể đọc qua liên kết này: https://asahilinux.org/2022/11/tales-of-the-m1-gpu/
      Có áp dụng phổ quát được hay không thì phải chờ xem trong vài năm tới
    • Có cảm giác mạnh rằng vì theo đuổi sự hoàn hảo mà bỏ lỡ cái tốt
    • Tôi đồng ý với ưu điểm của Rust, nhưng có xu hướng cho rằng lý trí khó thắng được sự thổi phồng
      Chi phí đó sẽ được xem là cần thiết để đón nhận tương lai và tiến bộ
      Tôi tự hỏi vì sao không giới hạn vào một tập con an toàn, thay vì lao vào một làn sóng khổng lồ đầy lỗi chưa biết và các thỏa hiệp
    • Nếu là mã bổ sung thì có thể nói không chỉ Rust mà bất cứ thứ gì cũng đưa thêm độ phức tạp vào
      Việc nó đã quá khổng lồ không có nghĩa là phải dừng đổi mới và chuyển sang trạng thái bảo trì vô thời hạn
      Như thuế trong đời thực, nếu chi phí ở một phía được dùng để bù cho vấn đề khác thì có thể đó không phải là lỗ ròng
      Chỉ nhìn vào một cuộc thảo luận đơn lẻ chưa có kết luận mà nói rằng nó sẽ không bù được vấn đề nào thì lập luận có vẻ hơi ngắn
  • Phần nói rằng tên của C API và Rust API lệch nhau nên nhìn mã C không biết lệnh gọi Rust tương đương là gì trông giống như cuộc chiến với quy ước đặt tên
    Đã từng có những trường hợp giải quyết tốt bằng cách giữ cùng tên, còn khi muốn tên thay thế thì để tên mới bọc tên cũ
    Dù vậy, đặt tên vẫn khó

    • Đây là một trong hai vấn đề lớn của khoa học máy tính
      Hai vấn đề còn lại là tính đồng thời và lỗi lệch một đơn vị