1 điểm bởi GN⁺ 2024-09-27 | 1 bình luận | Chia sẻ qua WhatsApp
  • Rust khởi đầu từ một nhận thức vấn đề: dù có an toàn bộ nhớ, hiệu năng, kiểu đại số và các lợi thế của Cargo, quá trình tiến hóa ngôn ngữ trên kênh stable đã chậm lại, khiến nó có cảm giác như “một sản phẩm thế hệ đầu vẫn chưa hoàn thiện”
  • Sau khi cộng đồng lớn lên, ra quyết định dựa trên đồng thuận trở thành nút thắt; các tính năng như coroutine dù đã được triển khai nhưng vẫn không thể dùng trong stable Rust trong thời gian dài tiếp tục tích tụ
  • “seph edition” giả định là một ý tưởng cho phép breaking change trong khi vẫn duy trì Rust hiện có và hệ sinh thái Cargo, nhằm thử nghiệm hệ thống effect, quyền ở thời điểm biên dịch, borrow trường struct, comptime, v.v.
  • Để giảm rủi ro chuỗi cung ứng, cần gắn capability cho các tính năng nhạy cảm như ghi file, mạng, FFI, unsafe, và bên gọi phải cho phép rõ ràng
  • Phần lớn đề xuất không tương thích với Rust hiện tại; chỉ riêng việc thêm capability cũng tạo ra điều kiện thất bại mới phá vỡ tương thích semver, nên cần một edition mới hoặc fork compiler

Vì sao Rust có cảm giác như “sản phẩm thế hệ đầu”

  • Ban đầu Rust là một ngôn ngữ rất hấp dẫn nhờ kiểu đại số, an toàn bộ nhớ không đánh đổi hiệu năng, và trình quản lý package hiện đại
  • Sau khoảng 4 năm sử dụng, ngôn ngữ này có cảm giác như luôn ở trạng thái “còn thiếu một chút”
  • Điểm bất mãn cốt lõi là tần suất tính năng mới đi vào stable Rust đã giảm, và tốc độ tiến hóa của ngôn ngữ cũng chậm đi đáng kể
  • Rust unstable book có khoảng 700 unstable feature, trong đó nhiều mục là thay đổi ở thư viện chuẩn
  • Coroutines là tính năng có RFC đã 7 năm tuổi và đã được triển khai trong compiler, nhưng người dùng stable Rust vẫn chưa thể dùng
  • Quy trình RFC của Rust trông giống như một nơi các ý tưởng hay bị lưu lại quá lâu
    • Cuộc thảo luận cải tiến Mutex được nêu như một ví dụ: 25 người để lại hơn 200 bình luận trong 2 năm nhưng không đi đến kết luận rõ ràng

Ý tưởng “seph edition” giả định

  • Có thể tưởng tượng một cách làm là fork compiler nhưng giữ nguyên code Rust hiện có, đồng thời thêm “seph” như một Rust edition riêng
  • Trong edition này, có thể cho phép breaking change, nhưng nếu compiler vẫn biên dịch được mainline Rust thì vẫn có thể tiếp tục dùng các crate hiện có của Cargo
  • Có năm trục lớn muốn thay đổi
    • Function trait và hệ thống effect
    • Capability ở thời điểm biên dịch
    • Thiết kế lại Pin, move và borrow struct
    • comptime kiểu Zig
    • Các cải tiến nhỏ về cú pháp và thư viện chuẩn

Function trait và hệ thống effect

  • Rust có trait cho struct, nhưng đề xuất cho rằng cũng cần có thể gắn trait/effect phong phú hơn cho function
  • Các thuộc tính mà một function có thể có được tóm lược như sau
    • Có thể panic hay không
    • Kích thước stack có cố định hay không
    • Có chạy đến hết hay thực hiện yield hoặc await hay không
    • Kiểu continuation khi là coroutine
    • Có phải pure function hay không
    • Có gián tiếp thực thi code unsafe hay không
    • Có đảm bảo kết thúc hay không
  • Nếu phơi bày parameter và kiểu trả về của function như associated type của function, có thể tham chiếu kiểu theo cách hiện không thể làm trong stable Rust
  • Ví dụ là code đặt tên trực tiếp kiểu trả về của function như some_iter::Output rồi đưa vào field của struct
  • Khi cần bảo đảm rằng một khối code nhất định tuyệt đối không panic, như trong Linux kernel, có thể dùng đánh dấu như #[disallow(Panic)] để kiểm tra khả năng panic bao gồm cả các lời gọi đệ quy
  • Compiler hiện đã xử lý các trait liên quan đến function như Fn, FnOnce, FnMut, nhưng dạng hiện tại bị đánh giá là quá nghèo nàn
  • Tài liệu tham khảo chi tiết hơn được liên kết tới bài viết và bài nói về effect system của Yoshua Wuyts

Giảm rủi ro chuỗi cung ứng bằng capability ở thời điểm biên dịch

  • Phần lớn dự án Rust kéo vào nhiều crate bên thứ ba, và ngay cả crate tiện ích nhỏ cũng có thể tạo rủi ro chuỗi cung ứng thông qua bản cập nhật độc hại
  • Cũng như unsafe là opt-in rõ ràng trong an toàn bộ nhớ, các tính năng nhạy cảm liên quan đến hệ thống file, mạng, FFI và raw pointer cũng nên phải được cho phép rõ ràng
  • Đề xuất là gắn marker tag cho các function nhạy cảm về bảo mật trong thư viện chuẩn
    • Ví dụ: std::fs::write(path, contents) ghi file vào đường dẫn tùy ý nên có tag như #[cap(fs_write)]
    • Compiler tự động taint toàn bộ call tree gọi function đó
  • Nếu crate bên thứ ba cần capability fs_write, bên gọi phải cho phép rõ ràng trong Cargo.toml hoặc bằng annotation tại điểm gọi
  • Nếu không cho phép, compiler có thể báo lỗi rằng foo::do_stuff() ghi vào hệ thống file cục bộ nhưng crate foo chưa được tin cậy với capability đó
  • Nhiều crate tiện ích như human-size hay serde không cần capability đặc biệt, nên ngay cả khi tác giả thêm code độc hại, hành vi ghi file hoặc truy cập mạng có thể bị chặn ở bước biên dịch
  • Một phương án thay thế là đổi API nhạy cảm để nhận một parameter Capability riêng
    • Ví dụ: std::fs::write yêu cầu một giá trị FsWriteCapability
    • Việc tạo object Capability bị giới hạn chỉ cho root crate
  • Cách này tăng boilerplate nhưng linh hoạt hơn, và cũng cần xử lý tương tự với script build.rs và block unsafe
  • Ngay cả trong tình huống crates.io bị hack và code cryptolocker được đưa vào serde, với cách dùng capability, lỗi biên dịch sẽ xảy ra trước khi code đó chạy trên máy của nhiều developer hoặc được đưa vào binary

Thiết kế lại Pin, move và borrow struct

  • Pin bị đánh giá là một hack phức tạp để đi vòng qua lỗ hổng của borrow checker, một band-aid sinh ra từ nỗ lực giữ tương thích ngược
  • Thứ thực sự cần được xem là gần với Move marker trait để đánh dấu các kiểu có thể di chuyển
  • Rust hiện không có trait Pin mà có Unpin!Unpin; dạng phủ định kép này khiến khái niệm khó hiểu hơn
  • Có phê bình rằng vì Pin chỉ áp dụng cho kiểu tham chiếu, code bọc bằng Box xuất hiện khắp nơi
    • Ví dụ có liên kết tới các helper library như Tokio stream wrapper, ouroboros, async-trait, self_cell
  • Các function nhận giá trị pinned như Future::poll(self: Pin<&mut Self>, ..) cũng trở nên phức tạp, và cần crate riêng để xử lý projection
  • Bên trong function, borrow checker quản lý biến ở các trạng thái “owned”, “borrowed”, “mutably borrowed”, nhưng các trạng thái này không trực tiếp hiển thị với programmer
  • Với async fn, compiler tạo hidden struct để lưu trạng thái tạm dừng, và khi đó có thể xuất hiện dạng một field borrow field khác
  • Rust không có cú pháp để biểu diễn rằng field của struct đang ở trạng thái borrowed, cũng không thể biểu diễn trực tiếp lifetime giữa các field
  • Hướng đề xuất là mở rộng borrow checker để có thể viết trực tiếp borrow trường struct
    • Ví dụ: cú pháp “local borrow” như y: &'Self::x Vec<usize>
    • Compiler biết rằng x đã bị borrow và áp dụng các ràng buộc giống như với biến borrowed bên trong function
  • Cú pháp như vậy cũng có thể dùng để xử lý self-referential struct hoặc các dạng như source: Stringast_nodes: Vec<&'Self::source str> trong AST
  • Struct có borrowed field sẽ không thể di chuyển nên sẽ không implement Move; xa hơn nữa cũng có thể cân nhắc trait Mover để tự di chuyển một cách an toàn

comptime kiểu Zig và vấn đề macro của Rust

  • Rust compiler về bản chất trông như một cấu trúc xử lý cùng lúc nhiều ngôn ngữ: Rust, ngôn ngữ Rust macro và cả proc macro
  • Điểm chính của đánh giá là bản thân Rust tốt, nhưng ngôn ngữ macro thì không tốt
  • comptime của Zig là cách compiler chạy một phần code ở thời điểm biên dịch thông qua một interpreter nhỏ
  • Function, parameter, if, loop có thể được đánh dấu là code thời điểm biên dịch, còn code non-comptime được emit vào chương trình thực
  • std print case study của Zig là ví dụ nhận format string làm parameter comptime, rồi phân tích cú pháp trong vòng lặp comptime để sinh code in ra
  • Việc triển khai println!() của Rust gọi các function nội bộ như format_args_nl, và function này được cho là hard-code trong compiler
  • Phê bình tiếp theo là trông như ngay cả tác giả Rust compiler cũng không muốn dùng ngôn ngữ macro của Rust

Các chỉnh sửa nhỏ về cú pháp và thư viện chuẩn

  • Có một mục muốn sửa vấn đề Range<T> implement Copy khi T: Copy
  • Vấn đề derive khi có associated type cũng được liệt kê là đối tượng cần sửa
  • Có đề xuất rằng biểu thức if let nên hỗ trợ logical AND
    • Dạng mong muốn: if let Some(x) = some_var && some_expr { }
    • Cách đi vòng hiện tại là match tuple (some_var, check_foo()), nhưng ngay cả khi some_varNone, check_foo() vẫn chạy, khác với hành vi short-circuit của if thông thường
    • Ví dụ: Playground
  • Ergonomics của raw pointer cũng được nêu là mục cần cải thiện
    • Với tham chiếu có thể viết myref.x, nhưng với pointer phải viết như (*myptr).x hoặc (*(*myptr).p).y
    • Quan điểm là code unsafe nên dễ đọc và dễ viết nhất có thể
  • Có đề xuất muốn đổi các built-in collection type để nhận rõ Allocator trong constructor thay vì dùng global allocator
  • async cũng cần cải thiện, nhưng phần này được để lại vì có quá nhiều nội dung, đủ để cần một bài riêng

Tương thích và khả năng thực thi

  • Phần lớn đề xuất không tương thích với Rust hiện tại
  • Ngay cả việc thêm security capability cũng tạo ra một kiểu điều kiện thất bại mới khiến crate phá vỡ tương thích semver, nên cần một Rust edition mới
  • Nếu là vài năm trước, có lẽ tác giả đã viết RFC, nhưng muốn tránh tình huống trải qua quy trình bình luận RFC dài trên GitHub rồi lại trở thành một ý tưởng nữa không được hiện thực hóa
  • Cuối cùng ý tưởng quay lại với việc tự fork compiler, nhưng vẫn còn ràng buộc thực tế là có quá nhiều dự án cần làm

1 bình luận

 
GN⁺ 2024-09-27
Ý kiến trên Hacker News
  • Trái với nhận định rằng quy trình RFC của Rust là nghĩa địa của những ý tưởng hay, tôi cho rằng việc đội ngũ cốt lõi Rust khiến việc thêm tính năng mới vào ngôn ngữ lập trình trở nên khó khăn là hướng đi đúng
    Cần ngăn bề mặt ngôn ngữ trở nên phình to, thiếu nhất quán và khó dự đoán. Ban đầu tôi cũng thích Swift, nhưng rồi cuối cùng đã bỏ cuộc khi những thứ như tên hàm trùng lặp kiểu isMultiple(of:), quy tắc phân tích cú pháp dấu ngoặc nhọn dành cho SwiftUI, các quy tắc về kiểu tham chiếu/kiểu giá trị và tính khả biến, cách viết tắt tham số closure, v.v. cứ tiếp tục tăng lên. Ý tưởng hay thì rất nhiều, nên tôi mong Rust được giữ gọn nhẹ hết mức có thể

    • Tôi là tác giả bài gốc. Khi dùng Rust, thường có những lỗ hổng kiểu tính năng X và tính năng Y đều được hỗ trợ riêng lẻ nhưng không thể dùng cùng nhau
      Ví dụ, ta có thể dùng hàm trả về impl Trait và struct có thể có các trường tùy ý, nhưng lại không thể đưa giá trị được trả về bằng impl Trait vào trường của struct. Lý do là không thể đặt tên cho kiểu đó. if a && b thì được, if let Some(x) = x cũng được, nhưng if let Some(x) = x && b thì không. Tôi mong những thứ như vậy được sửa. Tính theo số dòng mã của compiler thì Rust sẽ lớn hơn, nhưng xét theo độ phức tạp khi học và sử dụng, các lỗ hổng tính năng như thế lại khiến ngôn ngữ phức tạp hơn. Pin không phải là một việc quá lớn để triển khai trong thư viện chuẩn, nhưng tuyệt đối không phải là một tính năng gọn nhẹ, và gánh nặng nhận thức của nó là rất lớn. Tôi cho rằng một borrow checker phức tạp và mã Rust dễ đọc vẫn tốt hơn một compiler đơn giản nhưng ngôn ngữ khó dùng
    • Viết RFC dễ hơn triển khai, và số người có thể viết RFC cũng nhiều hơn. Vì vậy, ở bất kỳ mức độ phổ biến nào, hàng đợi RFC cũng tất yếu trông như nghĩa địa của những ý tưởng hay
      Ngay cả nếu 100% ý tưởng đang chờ đều được chấp nhận và cuối cùng được thực hiện, nó vẫn sẽ trông như vậy vì chênh lệch giữa tốc độ đi vào và tốc độ đi ra. Nếu muốn đi trước các tác giả RFC, tốt hơn là đừng viết RFC; hãy tạo một bản triển khai sẵn sàng cho production kèm tài liệu và kiểm thử, có thể được merge sạch sẽ vào tree
    • Nhìn chung tôi đồng ý, nhưng hằng liên kết generic của trait đang ở trạng thái nghịch lý: đã ổn định mà lại không thể dùng như hằng thực sự
      Không thể dùng làm const generic của kiểu khác, cũng không thể dùng làm độ dài mảng. Nếu chỉ cần một giá trị thì có thể định nghĩa hàm để trả về, nên hiện tại nó không hữu ích lắm. Nếu được hỗ trợ đúng cách, có thể loại bỏ các crate phụ trợ như generic_array, typenum trong thư viện mật mã. Dù vậy, tôi vẫn đồng ý rằng đội ngũ Rust cần thận trọng khi thêm tính năng
    • Rust, trong số các ngôn ngữ lập trình đã phần nào trở thành chủ lưu, hiện giống như Wagyu, nên không được bồi thêm mỡ đến mức không ăn nổi
    • Ý tưởng hay, theo định nghĩa, là thứ hiếm và quý
  • Tình hình phụ thuộc khá nghiêm trọng, nhưng có vẻ không nhiều người muốn thừa nhận. Ví dụ tôi thấy gần đây là crate cargo-watch
    Về cốt lõi, đây là một ứng dụng đơn giản theo dõi thay đổi tệp và chạy lại trình biên dịch, phần triển khai chưa đến 1.000 dòng. Thế nhưng nếu vendor các phụ thuộc vào, mã Rust phình lên gần 4 triệu dòng, trải trên hơn 8.000 tệp. Như vậy là quá mức đối với một trình theo dõi tệp đơn giản
    https://crates.io/crates/cargo-watch

    • Nếu việc tạo phụ thuộc bắc cầu quá dễ dàng, cộng với văn hóa “có thư viện thì nhất định phải dùng”, thì kết quả đó là tất yếu
      C/C++ gần như là trường hợp duy nhất trong các ngôn ngữ phổ biến mà trình quản lý gói kiểu npm không trở nên đại chúng, nên đa số thư viện thường tự hoàn chỉnh, hoặc có ít phụ thuộc và các phụ thuộc đó thường là tùy chọn. efsw là một trình theo dõi hệ thống tệp C++ dài 7.000 dòng, không có phụ thuộc. Các thư viện single-header trong mảng lập trình game như stb_*, cgltf, và Dear ImGui là những thứ dễ chịu nhất tôi từng dùng. Tôi cho rằng với trình quản lý gói mới, cấm phụ thuộc bắc cầu nhìn chung có thể có lợi. Các phụ thuộc thư viện lớn có thể để người dùng tự cài, cung cấp callback, hoặc biến thành tính năng chuẩn
      https://github.com/SpartanJ/efsw
      https://github.com/nothings/stb
      https://github.com/jkuhlmann/cgltf
      https://github.com/ocornut/imgui
    • Ngày nay, rừng phụ thuộc là cách phổ biến để hoàn thành công việc. Điều tốt nhất runtime có thể làm là chấp nhận điều đó, khiến nó nhanh và an toàn nhất có thể, đồng thời mở rộng thư viện chuẩn để hỗ trợ cả các dự án có phụ thuộc tối thiểu
      Riêng trình theo dõi tệp thì tôi nghĩ không hề đơn giản, nhất là khi hỗ trợ nhiều nền tảng
    • Đây là hiện tượng tự nhiên và không quá đáng sợ. Mọi đoạn mã đều đứng trên một dãy núi phụ thuộc chứa nhiều chức năng hơn rất nhiều so với thứ bản thân nó dùng
      Một phần của cargo watch kéo vào thư viện wrapper cho Win32 API; đó là các binding được tạo tự động cho lời gọi Win32, nên tất yếu rất lớn. Thư viện chuẩn của hầu hết ngôn ngữ cũng có hàng triệu dòng, và ứng dụng chỉ dùng một phần trong đó. Boost của C++ cũng to như quái vật, nhưng lập trình viên chỉ lấy vài phần mở rộng để dùng. Thay vì địa ngục kiểu npm, nơi một gói như isOdd hay is even cũng có thể làm hỏng cả hệ sinh thái, thì ít phụ thuộc lớn hơn, được nhiều người duy trì và dựa vào, vẫn tốt hơn; Rust nhìn chung gần với hướng đó hơn
    • Ở chỗ làm, chúng tôi có chủ ý loại bỏ và viết lại nhiều phụ thuộc, nhưng dù là 1.000 dòng hay 4 triệu dòng thì cả hai con số có vẻ đều không chính xác nếu dùng làm ước tính số dòng mã phù hợp cho dự án này
      Phần lớn phụ thuộc của cargo-watch dường như đến từ ba yêu cầu trực tiếp là clap, cargo_metadata, watchexec. clap kéo vào nhiều thành phần CLI theo từng nền tảng, còn cargo_metadata gần như bắt buộc phải kéo theo nhiều thứ thuộc hệ serde. watchexec thì có chỗ để cải thiện: nó phụ thuộc vào command-group, cũng do cùng tổ chức quản lý, và gói này lại bắt buộc cần Tokio. Vấn đề lớn hơn là rất khó sửa phụ thuộc quá mức của các crate khác. Nếu có crate bị trói vào một watchexec cũ, đôi khi có thể khớp bằng cách chỉnh Cargo.lock, nhưng thường thì khó và phải lách bằng [patch]. Sẽ tốt nếu có cách dễ dàng định nghĩa một “stand-in” thay thế cho một crate ở phiên bản cụ thể, nhưng với các trình quản lý gói hiện có thì đây có vẻ là một bài toán nghiên cứu lớn
    • Phần lớn số dòng có khả năng đến từ các crate Windows API được sinh ra. Chúng nổi tiếng là khổng lồ
  • Rust không còn là một ngôn ngữ non trẻ thú vị nữa, mà đang ở giai đoạn hướng tới được áp dụng rộng rãi. Việc phát triển tính năng chậm lại là điều tự nhiên và lành mạnh
    Lúc này, sai lầm thiết kế gây hại hơn nhiều so với tốc độ thấp. Rust thú vị không phải vì các tính năng hào nhoáng, mà vì nó là một lớp ngôn ngữ mới: an toàn bộ nhớ, không có garbage collection và sẵn sàng cho production. Việc nó thực sự được đưa vào những nơi quan trọng còn thú vị hơn việc làm cho ngôn ngữ tốt hơn, và điều đó sẽ dễ hơn nếu có niềm tin rằng nó được vận hành một cách thận trọng

    • Có thể nói JavaScript cũng đang ở giai đoạn vòng đời tương tự, nhưng trong 10 năm qua đã bổ sung rất nhiều tính năng mới tuyệt vời
      Spread operator, generator function, async, arrow function, leftpad, Date mới, danh sách các tính năng quan trọng là không có hồi kết. JS lâu đời hơn Rust rất nhiều, được dùng rộng rãi hơn rất nhiều, và có nhiều triển khai production nên mọi tính năng mới đều phải được triển khai khớp nhau. Đã từng có một giai đoạn trì trệ quanh ES5, nhưng khác biệt có vẻ nằm ở việc ủy ban tiêu chuẩn ECMAScript đã tỉnh táo lại
    • Ada/SPARK đã tồn tại từ lâu, nên tôi băn khoăn liệu đây có thật sự là một lớp ngôn ngữ mới hay không. SPARK còn đẩy tính an toàn đi xa hơn, bao gồm cả kiểm chứng hình thức
  • Vì các Rustacean có vẻ ám ảnh với việc viết lại mọi thứ bằng Rust, tôi thật sự đã nghĩ rằng một bài viết về viết lại Rust hẳn sẽ là một trò đùa châm biếm meta

    • Chuyện đó đã xảy ra từ thời tiền sử rồi. Ban đầu Rust được viết bằng OCaml, nhưng cuối cùng đã được viết lại bằng Rust
    • Tôi là tác giả bài gốc. Đó chính là tham chiếu tôi nhắm tới trong tiêu đề
    • Họ muốn khiến các bạn, hoặc chúng ta, viết lại mọi thứ bằng Rust. Không phải tự họ làm
    • Người trong giới ngôn ngữ lập trình cũng thích bootstrapping. Việc viết Rust bằng Rust không đến mức vô lý như vậy
  • Việc trước hết phàn nàn về quá trình ra quyết định chậm, rồi lại liệt kê các tính năng có lý do chưa ổn định hóa gần như không liên quan đến ra quyết định, có vẻ hơi lạ
    Ví dụ, coroutine đang bị kẹt vì có những trường hợp biên khó giải quyết cho đúng. Không phải trong compiler đã có một bản triển khai hoàn chỉnh “chỉ cần bật lên” là xong, mà là một bản triển khai chưa hoàn thiện, hoạt động trong nhiều trường hợp nhưng không thể bật trên bản ổn định. Function trait cũng đã được quyết định rõ ràng là sẽ không ổn định hóa ở hình thức hiện tại vì nhiều lý do kỹ thuật và do tương tác với các tính năng trong tương lai. Nếu quay về quá khứ có lẽ có những phần đã được thiết kế khác đi, nhưng phần lớn liên quan đến các quyết định từ thời kỳ đầu của Rust, khi đội ngũ và nguồn lực còn nhỏ hơn rất nhiều. Có thể có quan điểm rằng vì bây giờ ta có thể chọn tốt hơn nên nên tạo một kiểu đứt gãy như Rust 2.0, nhưng vì thảm họa chuyển từ Python 2 sang 3 quá lớn, nhiều người cho rằng chịu đựng các góc cạnh thô ráp vẫn tốt hơn. Nếu xem newsletter hằng tuần của Rust, các quyết định phê duyệt RFC và ổn định hóa vẫn được xử lý mỗi tuần. Đúng là có những trường hợp mất quá nhiều thời gian, nhưng vấn đề thiếu người, phối hợp và thời gian thường khó giải quyết hơn vấn đề kỹ thuật

  • Bài này cũng nên đọc cùng câu trả lời trên Reddit của Josh Triplett. Một trong những ví dụ cốt lõi của bài, Mutex, đơn giản là sai
    https://old.reddit.com/r/rust/comments/1fpomvp/rewriting_rus...
    Sửa: cùng bình luận đó cũng có ở đây
    https://news.ycombinator.com/item?id=41655268

  • Khi học Rust, điều đầu tiên tôi cảm nhận là dường như gần như mọi tính năng có thể tưởng tượng được đều đã có trong đó
    Điều đó không có nghĩa là nhóm Rust chưa từng từ chối thứ gì, nhưng mọi người vẫn muốn có thêm nhiều tính năng hơn. Một số là chính đáng, nhưng một số lại trông như kiểu phải đưa vào ngôn ngữ một tính năng chỉ 2% lập trình viên dùng, trong một ngôn ngữ chỉ 1% lập trình viên hiểu được. Một ngôn ngữ phức tạp không cần phải phức tạp hơn nữa. Zig đơn giản hơn và có lẽ nhanh hơn, cộng đồng cũng ít drama hơn nhiều. Mong Zig nhận được nhiều tài trợ hơn

    • Có thể bạn sẽ ngạc nhiên khi biết có khá nhiều tính năng do những người bất kỳ đề xuất rồi bị cộng đồng Rust từ chối
      Rust không cố nhét vào mọi tính năng có thể. Chỉ là nếu không hiểu rõ GAT hay TAIT giải quyết vấn đề gì thì có thể cảm thấy như vậy. Zig có thể là một ngôn ngữ hiện đại tốt, nhưng nếu mục tiêu là an toàn bộ nhớ thì nó không phải là lựa chọn
    • Tôi là tác giả bài gốc. Tôi cho rằng ngân sách độ phức tạp của Rust đang bị dùng nhiều vào sai chỗ
      Ví dụ, tương tác giữa Pin và future thêm vào ngôn ngữ một mức độ phức tạp phi lý, và tôi nghĩ một phần trong đó là không cần thiết. Tôi ước có một ngôn ngữ giống Rust nhưng hoàn toàn không có Pin. Borrow checker có lẽ cũng có cách đơn giản hóa ở cả cú pháp lẫn triển khai, nhưng tôi vẫn chưa nghĩ cụ thể. Hiện tại trừ khi fork ngôn ngữ thì khó thay đổi, nhưng Rust sẽ không phải là ngôn ngữ cuối cùng dùng borrow checker. Tôi kỳ vọng thế hệ tiếp theo còn nhiều dư địa cải thiện mà không cần tạo ra một ngôn ngữ lớn hơn
    • Nếu có thêm tiền, có thể sẽ thu hút những kiểu người không tốt. Sự tệ hại và khả năng gây hại của họ đôi khi chỉ lộ ra khi đã quá muộn
    • Tôi tò mò bạn đang nói đến drama nào trong cộng đồng Rust
      Tôi thấy có drama liên quan đến Rust, nhưng thường nó đến từ phía chống lại việc dùng hoặc chấp nhận Rust. Như vụ ồn ào Rust for Linux gần đây. Dường như đó không phải chuyện thường gặp bên trong cộng đồng, nhưng có thể tôi đã bỏ sót. Zig rất tuyệt, nhưng vẫn chưa sẵn sàng cho production
    • Nghĩa địa các tính năng bị chôn trong nightly thực ra khá lớn. Ngay cả những tính năng quan trọng như specialization cũng bị stuck ở đó mãi
  • Tác giả của bình luận được liên kết đã phân tích rộng rãi các primitive đồng bộ hóa của nhiều ngôn ngữ, rồi viết lại các primitive đồng bộ hóa như MutexRwLock của Rust để trên từng hệ điều hành lớn chúng dùng trực tiếp các primitive cấp OS bên dưới
    Họ dùng những thứ như futex của Linux để làm cho chúng nhanh hơn, nhỏ hơn và nhìn chung tốt hơn; trong quá trình đó về cơ bản cũng đã viết một cuốn sách về lập trình song song trong Rust. Nó cũng hữu ích cho lập trình song song không phải Rust
    https://www.oreilly.com/library/view/rust-atomics-and/978109...
    Cũng không phải là suốt 7 năm chỉ chơi bời quanh coroutine. Rust đã thêm các hàm bất đồng bộ, trait có thể chứa hàm bất đồng bộ, nhiều tính năng cần thiết để chuẩn hóa AsyncWriteAsyncRead, và trên nightly cũng đã có triển khai generator. Hiện vẫn đang thảo luận liệu có chấp nhận độ phức tạp của coroutine hoàn toàn tổng quát hay dừng ở generator. Một số tính năng như AsyncIterator thì chậm, nhưng cũng có rất nhiều công việc đang được thực hiện tích cực. Lúc nào cũng thú vị khi một bên nói ngôn ngữ quá chậm, còn bên kia lại phàn nàn là quá nhanh
    Trait hàm và hệ thống effect gần đây cũng có những đợt thăm dò thiết kế lớn, và họ muốn đưa ra giải pháp để không phải viết hàm nhiều lần cho từng tổ hợp async, try, const. Sandbox crate độc hại không phải là vấn đề ở cấp ngôn ngữ; nó cần sự kết hợp giữa verifier và sandbox runtime, và WebAssembly Component có vẻ hứa hẹn hơn. Tuy vậy, họ rất quan tâm đến các capability tại thời điểm biên dịch như lựa chọn allocator hay runtime bất đồng bộ, giả định nền tảng 64-bit; còn sandbox proc macro thì họ muốn không phải để ngăn hành vi ác ý mà để caching chính xác
    Struct tự tham chiếu không phải là vấn đề cú pháp mà là vấn đề cực kỳ khó để borrow checker xử lý. Partial borrow đã được hỗ trợ trong capture của closure, nhưng mấu chốt là cách duy trì semantic versioning ổn định khi phơi nó ra API công khai. Những phương án như “borrow group” có tên có vẻ nhiều triển vọng. Phía comptime cũng đang được triển khai theo nhiều hướng, và gần đây cũng đã viết RFC để tăng cường macro_rules. impl của Range đã đang được thực hiện, gắn với thay đổi không tương thích thông qua edition. Việc kết hợp if let với AND logic có tính năng unstable và đã gần được ổn định hóa. Cũng đã có nhiều đề xuất cải thiện cú pháp truy cập field qua pointer, nhưng việc mở rộng thêm bề mặt ngôn ngữ là lợi hay hại vẫn là câu hỏi mở. Nhờ các edition của Rust, nếu thiết kế đủ thuyết phục thì có khá nhiều thứ có thể thay đổi. 700 tính năng unstable là vấn đề thực sự, nên cần một công việc lớn để dọn những thứ ít có khả năng được ổn định hóa

    • Việc phát triển các package cơ bản ở cấp thư viện chuẩn nên được làm như thế này. Những ngôn ngữ hiện đang dùng sẽ còn được dùng thêm nhiều thập kỷ, và nếu bây giờ đưa ra các quyết định chậm nhưng tốt thì về sau sẽ tiết kiệm được nhiều thời gian hơn rất nhiều
    • Đã có nhiều thảo luận về sandbox cho proc macro và build script
      Macro mang tính khai báo hơn, việc ủy quyền logic của crate -sys cho thư viện dùng chung, cfg(version) / cfg(accessible) sẽ giảm đáng kể nhu cầu người dùng phải tự triển khai, nhưng runtime thì vẫn còn đó. Càng nghĩ thì ACL của cackle càng có vẻ là một cách mở rộng tốt để theo dõi tác vụ trên toàn bộ proc macro, build script và mã runtime, cũng như audit việc sử dụng cây phụ thuộc. Tôi nghe nói cargo-redpen cũng đang phát triển thành công cụ audit lời gọi, nhưng tôi đang hình dung một mức cao hơn như cackle
      https://github.com/cackle-rs/cackle
    • Tôi là tác giả bài gốc. Rất vui khi được nghe chi tiết góc nhìn của người trong cuộc
      Tôi hiểu việc mọi người phàn nàn rằng Rust đã là một ngôn ngữ lớn nên không muốn nó lớn thêm, nhưng việc giữ nguyên triển khai async nửa chín nửa sống hiện tại không làm ngôn ngữ nhỏ hơn hay đơn giản hơn. Nó chỉ làm ngôn ngữ tệ đi mà thôi. Partial borrow cũng sẽ rất tốt nếu hoạt động được ít nhất trong cùng một crate, chứ không nhất thiết phải là API công khai. Đây là vấn đề liên tục gặp phải khi lập trình thực tế. Tôi cũng thắc mắc vì sao sandbox crate độc hại lại không thể làm ở cấp ngôn ngữ. Nếu một hàm không có mã bên thứ ba unsafe trong cây lời gọi và cũng không thực hiện system call, thì nó chỉ có thể xử lý các đối số đã được truyền vào, biến cục bộ và global nằm trong scope. Nếu có thể gia cố bức tường này để dùng làm ranh giới bảo mật, có vẻ có thể giảm mạnh rủi ro chuỗi cung ứng phụ thuộc
  • Sứ mệnh của Rust ngay từ đầu đã là việc khó: kết hợp hiệu năng, an toàn và khả năng biểu đạt; khi Mozilla rút đi thì Rust đã mất chế độ nhà sáng lập, và phần lớn đội ngũ cốt lõi ban đầu cũng đã rời đi, nên việc tiến triển chậm lại không có gì đáng ngạc nhiên
    Cá nhân tôi thấy như vậy vẫn tốt hơn là đi sai đường

  • Nếu viết lại Rust, có lẽ tôi sẽ đi theo hướng bớt tính năng chứ không phải thêm tính năng
    Kiểu như QBE đối với LLVM: cung cấp 70% Rust bằng 10% lượng mã. Có thể làm được nếu bỏ macro và một số tính năng ít dùng
    https://c9x.me/compile/

    • Không có ý hạ thấp QBE, nhưng mục tiêu ban đầu của QBE là cung cấp 90% hiệu năng bằng 10% lượng mã, rồi sau đó đổi thành 70%
      Dù là Rust hay thứ gì khác, trước khi thực sự thử thì rất khó biết khả thi đến mức nào