←back to thread

452 points birdculture | 5 comments | | HN request time: 0.695s | source
Show context
sesm ◴[] No.43979679[source]
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.
replies(5): >>43979717 #>>43979806 #>>43980063 #>>43982558 #>>43984758 #
1. abirch ◴[] No.43979717[source]
I 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/...

replies(1): >>43980072 #
2. sesm ◴[] No.43980072[source]
> 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.

replies(3): >>43980130 #>>43982034 #>>43984197 #
3. landr0id ◴[] No.43980130[source]
>> 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.

4. BlackFly ◴[] No.43982034[source]
I 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.

5. echelon ◴[] No.43984197[source]
Rust 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.