5 ms·
Is there a concise document that explains major decisions behind Rust language design for those who know C++? Not a newbie tutorial, just straight to the point:
by sesm 1y ago
Is there a concise document that explains major decisions behind Rust language design for those who know C++? Not a newbie tutorial, just straight to the point: why in-place mutability instead of other options, why encourage stack allocation, what problems with C++ does it solve and at what cost, etc.
- abirch 1y agoI think the major decisions behind Rust is being explicit and making the programmer make decisions. No NULLs, no Implicit conversions, no dangling pointers. Lifetimes, Optional, Results, each Match branch needs to exist, etc. Side note: Stack allocation is faster to execute as there's a higher probability of it being cached. Here is a free book for a C++ to Rust explanation. https://vnduongthanhtung.gitbooks.io/migrate-from-c-to-rust/content/ https://vnduongthanhtung.gitbooks.io/migrate-from-c-to-rust/...
- sesm 1y ago> being explicit and making the programmer make decisions Why RAII then? > C++ to Rust explanation I've seen this one. It is very newbie oriented, filled with trivial examples and doesn't even have Rust refs to C++ smart pointers comparison table.
- landr0id 1y ago>> being explicit and making the programmer make decisions >Why RAII then? Their quote is probably better rephrased as _being explicit and making the programmer make decisions when the compiler's decision might impact safety_ Implicit conversion between primitives may impact the safety of your application. Implicit memory management and initialization is something the compiler can do safely and is central to Rust's safety story.
- BlackFly 1y agoI would say that RAII is very explicit: Resource Acquisition Is Initialization. When you initialize the struct representing the resource you are acquiring the resource. If you have a struct representing a resource you have the resource. Knowing this, you are also acquiring a call to drop when it goes out of scope. I would argue that the difference here isn't explicitness. Instead, I would argue that rust is favoring a form of explicitness together with correctness. You have to clean up that resource. I have seen arguments that you should be allowed to leak resources, and I am sympathetic, but if we agree on explicitness as a goal then perhaps you might understand the perspective that a leak should be explicit and not implicit in a the lack of a call a some method. Since linear types are difficult to implement auto-drops are easier if you favor easily doing the correct thing. If you want to leak your resource, stash it in some leak list or unsafe erase it. That is the thing that should be explicit: the unusual choice, not all choices and not the usual choice alone. But yeah, the drop being implicit in the explicit initialization does lead to developers ignoring it just like a leak being implicit if you forget to call a function often leads to unintentionally buggy programs. So when a function call ends they won't realize that a large number of objects are about to get dropped. To answer your original question, the rationale is not in one concise location but is spread throughout the various RFCs that lead to the language features.
- echelon 1y agoRust is RAII at the compiler level. It's the language spec itself. That's probably the best way to describe the design and intent of Rust's memory model. When you create a thing, you allocate it. That thing owns it and destroys it, unless you pass that ownership onto something else (which C++ RAII doesn't do very cleanly like Rust can). Then it does some other nice things to reduce every sharp edge it can: - No nulls, no exceptions. Really good Option<T> and Result<T,E> that make everything explicit and ensure it gets handled. Wonderful syntactic sugar to make it easy. If you ever wondered if your function should return an error code, set an error reference, throw an exception - that's never a design consideration anymore. Rust has the very best solution in the business. And it does it with rock solid safety. - Checks how you pass memory between threads with a couple of traits (Send, Sync). If your types don't implement those (usually with atomics and locks), then your code won't pass the complier checks. So multithreaded code becomes provably safe at compile time to a large degree. It won't stop you from deadlocking if you do something silly, but it'll solve 99% of the problems. - Traits are nicer than classes. You can still accomplish everything you can with classic classes, but you can also do more composition-based inheritance that classes don't give you by bolting traits onto anything you want. - Rust's standard library (which you don't have to use if you're doing embedded work) has some of the nicest data structures, algorithms, OS primitives, I/O, filesystem, etc. of any language. It's had 40 years of mistakes to learn from and has some really great stuff in it. It's all wonderfully cross-platform too. I frequently write code for Windows, Mac, and Linux and it all just works out of the box. Porting is never an issue. - Rust's functional programming idioms are super concise and easy to read. The syntax isn't terse. - Cargo is the best package manager on the planet right now. You can easily import a whole host of library functionality, and the management of those libraries and their features is a breeze. It takes all of sixty seconds to find something you want and bring it into your codebase. - You almost never need to think about system libraries and linking. No Makefiles, no Cmake, none of that build complexity or garbage. The compiler and cargo do all of the lifting for you. It's as easy as python. You never have to think about it.
- NobodyNada 1y agoThis might not be exactly what you're looking for, but I really like "References are like jumps": https://without.boats/blog/references-are-like-jumps/ https://without.boats/blog/references-are-like-jumps/
- jandrewrogers 1y agoRust has better defaults for types than C++, largely because the C++ defaults came from C. Rust is more ergonomic in this regard. If you designed C++ today, it would likely adopt many of these defaults. However, for high-performance systems software specifically, objects often have intrinsically ambiguous ownership and lifetimes that are only resolvable at runtime. Rust has a pretty rigid view of such things. In these cases C++ is much more ergonomic because objects with these properties are essentially outside the Rust model. In my own mental model, Rust is what Java maybe should have been. It makes too many compromises for low-level systems code such that it has poor ergonomics for that use case.
- Ar-Curunir 1y ago> However, for high-performance systems software specifically, objects often have intrinsically ambiguous ownership What is the evidence for this? Plenty of high-performance systems software (browsers, kernels, web servers, you name it) has been written in Rust. Also Rust does support runtime borrow-checking with Rc<RefCell<_>>. It's just less ergonomic than references, but it works just fine.
- jandrewrogers 1y agoAnyone that works on e.g. database kernels that do direct DMA (i.e. all the high-performance ones) experiences this. The silicon doesn’t care about your programming language’s ownership model and will violate it at will. You can’t fix it in the language, you have to accept the behavior of the silicon. Lifetimes are intrinsically ambiguous because objects have neither a consistent nor persistent memory address, a pretty standard property in databases, and a mandatory property of large databases. Yes, you can kind of work around it in idiomatic Rust but performance will not be anything like comparable if you do. You have to embrace the nature of the thing. The near impossibility of building a competitive high-performance I/O scheduler in safe Rust is almost a trope at this point in serious performance-engineering circles. To be clear, C++ is not exactly comfortable with this either but it acknowledges that these cases exist and provides tools to manage it. Rust, not so much.
- 1y ago
- scotty79 1y agoIn-place mutability and stack allocation are for speed. This made them have variables and values inside them both as separate first-class citizens. The entire borrow checker is just for tracking variables access so that you don't need to clone values. I'd say that given this one guiding decision there are very little arbitrary ones in Rust. The rest of it pretty much had to be made exactly the way it is. Rust is more of a discovery than a construct. C++ is just Rust without any attempt at tracking variable access and cloning which leads to a mess because people are too terrible at that to do that manually and ad-hoc. So Rust fixes that.
- steveklabnik 1y agoI’m not aware of one, but I’m happy to answer questions. > in-place mutability I’m not sure what this means as. > why encourage stack allocation This is the same as C++, things are stack allocated by default and only put on the heap if you request it. Control is imporrant > what problems with C++ does it solve and at what cost The big one here is memory safety by default. You cannot have dangling pointers, iterator invalidation, and the like. The cost is that this is done via compile time checks, and you have to learn how to structure code in a way that demonstrates to the compiler that these properties are correct. That takes some time, and is the difficulty people talk about. Rust also flips a lot of defaults that makes the language simpler. For example, in C++ terms, everything is trivially relocatable, which means Rust can move by default, and decided to eliminate move constructors. Technically Rust has no constructors at all, meaning there’s no rule of 3 or 5. The feeling of Rust code ends up being different than C++ code, as it’s sort of like “what if Modern C++ but with even more functional influence and barely any OOP.”