# Rust Design Principles nikomatsakis --- Rust has always had *slogans*... ---- ![Stability as a deliverable](https://i.imgur.com/XKapI8I.png) ---- ![Fearless concurrency with Rust](https://i.imgur.com/f2iSvlC.png) ---- ![Rust Once, Run Everywhere](https://i.imgur.com/p6w1Lyo.png) ---- ![Abstraction without overhead](https://i.imgur.com/zNZUqQR.png) ---- Each slogan captured some aspect of our design --- We've also had our share of fearsome debates... ---- ![](https://i.imgur.com/1qI3S04.png) --- ![](https://i.imgur.com/gQzetrx.png) **482 comments!** --- How can we capture the tradeoffs we've developed? ---- Rust Design Principles is an attempt to do that. ---- Grew out of the [design tenets](https://rust-lang.github.io/wg-async-foundations/vision/tenets.html) from the [async vision doc](https://rust-lang.github.io/wg-async-foundations/) --- ### Six principles * Safety first * You *can* have nice things * Transparent and predictable * Orthogonal, composable, and extensible * Expose all system capabilities * Hard to misuse --- ### Safety first Safe Rust code is memory safe, data-race free, and free of other kinds of [undefined behavior](https://en.wikipedia.org/wiki/Undefined_behavior). Unsafe Rust code is meant to be well-encapsulated and exposed via a safe interface. ---- ### Example: Ownership and borrowing ---- ### Where we compromise on this We don't. We'll even break stability. --- ### You *can* have nice things The high-level features in Rust are scalable, performant, and full-featured. We don't expect our users to "drop down" to low-level code, except in extreme cases. ---- ### Example: Iterators ```rust values.iter().map(|x| x * 2).sum() ``` * In Rust (or C++), as fast as a for loop * Or faster! No bounds checks. * In many other languages: * Costs performance ---- ### More examples * Iterators, async fn, etc * Closures that desugar to struct fields * Autoderef and `.` syntax * Operator overloading ---- ### Where do we give up on "nice things"? * Bounds checks: * Using `vec[i]` in C is faster, but we value safety more. * Unsafe code to skip indexing checks ensures we still "expose all capabilities" --- ### Transparent and predictable Rust avoids hidden control flow, silent memory allocation, or complex built-in operations. You don't need a lot of context to figure out what some Rust code will do when it runs or what machine code it will generate. ---- ### Example: No garbage collector ---- ### Example: Copy vs Clone ```rust some_function(y); ``` * In Rust, always does a memcpy * In C++, could invoke a copy constructor * Arguably, not entirely true to the spirit of principle ---- ### More examples * Unwind, `?` operator * Exceptions make it harder to reason about control flow * No implicit allocation * Leads to unexpected performance cliffs * The "Predictability" rules of the API guidelines * e.g., Smart pointers do not add inherent methods ---- ### Where do we give up on transparency? * To be used, must be "nice enough". * Optimizations often unpredictable: * e.g., reordering struct fields and enums to enable composition --- ### Orthogonal, composable, extensible Rust encourages components that can be reused in many ways and in many environments. Rust programs work across mainstream architectures and operating systems by default. We encourage exploration, but we also recognize that exploration sometimes requires standardization. ---- ### Example: cargo, crates.io ---- ### Example: Coherence * Coherence exists to ensure that any two crates can be combined ---- ### Example: Portability ---- ### More examples * Minimal standard library * Global allocator, which permits convenient interop * Language constructs like `usize` meant to ensure code builds on both 32- and 64-bit architectures ---- ### Where we compromise on this * No garbage collector * Auto traits and auto trait leakage: * prefer "nice things" over stronger semver --- ### Expose all system capabilities Whatever it is you want to do, you should be able to do it in Rust, though it may require jumping through some hoops. ---- ### `"C"` vs `"C-unwind"` ---- ### SIMD intrinsics ---- ### More examples * Inline assembly * `OsString` * `#[repr(C)]`, `extern "C"`, `extern "C-unwind"` ---- ### Where we compromise on this * For "nice things": * support longjmp, but only in limited cases * For composition: * Portable abstractions * std library exists --- ### Hard to misuse We take every opportunity to help users detect bugs and errors in their programs. ---- ### Example: Exhaustive pattern matching ```rust match foo { Some(x) => ..., } ``` * In Rust, requires a `_` case * In other languages, would generate an exception * Hard to know if something was elided on purpose ---- ### Example: dead code lints * Helps catch bugs and half-finished refactorings * Kind of annoying when you're writing code, though ---- ### Other examples * Overflow checks on math operations * `assert!` on by default * `Path::display` ---- ### Where we compromise on this * Overflow checks disabled in release * Because "nice things" take priority --- ### What now * Evolving, still a WIP * Planning to try applying these principles to ongoing design questions * Would like to hear from other teams on how well they fit (or don't) * Would like to derive other rules from these ---- ### Questions for you * Something not covered by these principles? * Not sure about the ordering? * Have a counterexample? ---- ### The principles (again) * Safety first * You *can* have nice things * Transparent and predictable * Orthogonal, composable, and extensible * Expose all system capabilities * Hard to misuse
{"metaMigratedAt":"2023-06-16T02:42:54.015Z","metaMigratedFrom":"YAML","title":"3: Rust design principles","breaks":true,"contributors":"[{\"id\":\"27d8f2ac-a5dc-4ebe-8c31-cc45bcd8447e\",\"add\":10932,\"del\":5069}]"}
    1065 views
   owned this note