Rust by Example
rustbyexample.com
rustbyexample.com
However if you want to create any graph like data structures you need to go into unsafe rust.
For example, the std doubly linked list uses unsafe with error handling to do inserts because the compiler cannot be sure that an insert is safe.
In my opinion Rust's safety is overhyped and entirely dependent on good implementation of unsafe code. And judging from the liked list example there is no way for all rust code to be intrinsically safe.
That's short-sighted. Of course it's "entirely dependent on good implementation of unsafe code".
But that's inconsequential, as unsafe code in a library, especially one used by everybody, is much easier to make safe than unsafe code all around.
And after you trust an unsafe lib you use to be sound, your own safe-only code can never breach that safety. You'll be guaranteed that any issue will be on the unsafe part.
It's all about compartmentalization.
...as is true of any safe language, which is dependent on a good implementation of its compiler or interpreter. Rust just happens to move some of that into the language where it can be extended.
For about a year, I've been regularly using Rust to write real software that I actually use, and I've never had to write unsafe code. I'm trusting other people to do it, which is never not the case, no matter what programming language I'm using.
My main point is that safety rests upon the library correctness. And I can see the value of the interlocks this provides. However it is not infallible as the word safe suggests.
I'll admit that I have not grokked rust, but I did give it a reasonable try. Perhaps all my C hacking has ruined me. :)
I don't disagree with that. Sometimes I don't totally love how the Rust people market the language, and I can see how the statements they make about safety could rub their target audience the wrong way, but I don't know how I'd do it better. I really do think the language does a great job helping programmers write fast, safe, bug-free code... once they get over the learning curve.
If you ever feel like taking another crack at it, this is the reference that made the memory model stuff (which is the trickiest bit IMO) click for me:
My main issue is that I found creating graphs is unwieldy. Where in C or Python I can just whip one up.
Is there a reasonably easy way to do it without depending on a library (like petgraph)?
https://github.com/postmates/cernan
Cernan has to be sparing in its memory consumption, be a good citizen with regard to its CPU impact–at Postmates we deploy it as a ride-along with application servers--and be correct even though there's changes being made at a punishing pace.
When I started cernan I'd been following Rust for a while but had _not_ written anything of note in it. My estimation, though, was that Rust would be a better fit for cernan than C++:
* It'd be easier for colleagues knowing neither Rust nor C++ to review Rust code.
* If I got hit by a bus, the Rust compiler would make stronger behaviour constraints for me.
You can see in the first month https://github.com/postmates/cernan/commits/master?after=Y3V... that I flailed around a bit. I was internalizing the borrow checker's view of Rust's memory model, getting used to the tooling and the ecosystem of crates available. One thing I particularly had issues with from the get go and have _kind of_ resolved are many wee allocations inside cernan. In some sense they're inevitable but my early ignorance exacerbated the problem: I'd clone to convince the borrow checker to let me move on, would construct new containers rather than pass new ones in to be refilled and the like. Also, strings. Lots and lots of strings everywhere.
After the first month, toward the back half of the second I felt like I was flying finally. Still plenty I needed to internalize or learn about but I had a cohesive mental model in which to incorporate new information.
I've been teaching myself Rust in the evenings for a couple of weeks now and have found the process maddeningly confusing and slow. Even with the Book, which is fine, I ended up getting into rage induced clone/to_owned/etc keyboard mashing dances with the borrow checker. If it weren't so easy to do this kind of dev with emacs, I might have given up earlier.
On the other hand I occasionally get these moments where my code just "clicks" and it feels good and _seems_ elegant. But, pedagogically speaking, at this point I'm just trying to learn through immersion, not through theory and understanding.
Honestly I'd probably be way done with this little project if I had started in Go, but I think I'm going to stick with Rust for a while.
Oh hey, that's a pretty good way of putting it. For me, internalizing the borrow checker is where I had the hardest time. Much of what the borrow checker formalizes is best-practices in C++ but it's a surprisingly different experience to have a robot come and check what you ought to have been doing anyhow. Maybe I found it even harder to adapt because while my C++ is passable I'm no guru.
But! Immersion for sure works and I'm _really_ glad I stuck it out. Now that I'm on the other side I can see that the borrow checker is a novel invention and novel inventions are _very_ difficult to explain at first.
This is where I'm at right now. When the borrow checker yells at me, I find I can generally fix it with a .clone() or a .to_string(). I'm new enough with the borrow checker that I'm not quite sure when such cloning is necessary and when I'm unnecessarily allocating more memory -- or more importantly, what I should be doing instead when it's not necessary -- but I hope that as I write more code, I'll get better at it :)
A bad habit that I've fallen into is to needlessly allocate in order to avoid having to think about lifetimes. As in, better change that struct to make that str a String -- and then not have to worry about lifetimes at all. That's just laziness, though...
Make it work, then make it correct, then make it fast. Chances are you'll never need that last step for most code you write.
Edit: another thing you'll learn with practice is when to make the right trade offs. Rust has lots of types in the standard library that allow you to pick and choose which guarantees you want the compiler to uphold and whether to do it at compile time or run time. Check out "Choosing your Guarantees" in the new version of the rust book [1]
[1] https://doc.rust-lang.org/beta/book/choosing-your-guarantees...
You have to "notify the memory allocator" in every language but in Rust you don't pay for the extra GC overhead unless you explicitly opt into RC, Arc, or your own GC. The point is that in a low level language, allocating and copying to maintain safety guarantees is fine because you have finr grained control over what you pay for. For a beginner, it's more important to get a feel for the borrow checker and cloning allows someone to get a positive feedback loop until they figure out how to structure their code to work with the borrow checker instead of against it.
Cloning is definitely a perfectly good way to get started, but it shouldn't be dressed up as something it's not nor should Rust itself.
I'm still missing an IDE that spits out inferred types including generics when hovering in the middle of chained method calls. Especially in iterators it's often hard to know what it's currently passsing as arguments into some closure.
That situation is much much better in java-land.
(A more general description of this work starts around 40:09)
It took me a month (of my free time) to start feeling like I can write code in Rust. It felt like I was learning programming all over again.
There were several reasons for that: first, I assumed that Rust can be used the same way as Javascript, with callbacks everywhere, and second, I was used to OO patterns that can not work in Rust. So, to overcome that, I had to realize that while Rust may have modern features, it is fundamentally a low-level language; and I had to really understand what the OO patterns actually give me to find the alternatives in Rust. I think that these things have made me a better programmer overall.
I remember these distinct stages.
Rust is low-level stage: It took a while to realize that Rust structs live on stack, and the pointer to such struct can not be freely passed around like an object, because the stack frame will be gone at the end of a function.
I found out how to do virtual dispatch: the fact that borrowed traits have virtual dispatch table was missing from docs at the time (they are called Trait Objects). This is what I was actually missing from OO: a way to use the interfaces without putting everything into enums, and have a way to invert dependencies.
There is a borrow checker, but you need to think in ownership: Again, I was trying to use references as if they were objects in OO language, while the primary model is ownership. I wrote a bog post at that time discussing this: http://nercury.github.io/rust/guide/2015/01/19/ownership.htm...
Rust is more functional than it seems: At that point I found that you can model anything in Rust if you can express it as a data-flow from the input to output. Turns out, anything can be expressed as the data flow. The data that flows can be uniquely mutable, processed in parallel. I had to ditch thinking in highly shared hierarchies of objects and focus on flows that transform one set of structures to other sets of structures.
This is coming from a background of mostly C/Java.
Once it clicks it is very simple, but developing the intuition and breaking old habits is the frustrating part.
I'd say another two weeks before I was using lifetimes properly, traits, enums, etc in 'idiomatic' ways.
All in all, 4 weeks before I felt productive.
I come from C++, which helped and hurt. On the one hand, move semantics weren't new - but C++ move semantics felt like nothing* compared to Rust's. It actually really helped me internalize why you would want them in C++.
I knew that returning a pointer to the stack was bad in C++. In rust, I actually started doing that (just parent stacks), but safely.
* Such as
self.foo(&self.bar)http://cglab.ca/~abeinges/blah/too-many-lists/book/
for helping to grok the memory model. I tried to implement each chapter's data structure before I read the chapter so that I would really understand what problem the introduced memory constructs were solving.
It's definitely not an easy language to learn, but once you get past the memory model stuff I think the learning curve at least gets a little flatter.
Servo, for example, doesn't deal with this; it's a web browser.
Think of the use cases where you'd use C or C++, and Rust should work there.
You probably can rewrite Ruby scripts in Rust if you want, but the productivity benefit depends on the kind of script. But for the purposes of learning the language it doesn't really matter.
I could see it for small modular parts that are better suited for native code and when one doesn't want to use C, perhaps, or, of course, for infrastructure-level projects like Servo.
I just wonder if I'm barking up the wrong tree because much of the Rust interest I see seems more heavily weighted in infrastructure and backend, not in user-facing code.
I'd be really curious to know if website format exists for other languages.
Can you give some examples of "this stuff"? As a recent entrant to the world of C++ I would be curious to hear those areas I should investigate to get to that point. You mentioned breaking cycles with weak references so it is it mostly understanding how C object model is implemented and/or anything else? Any resources or book you found helpful to find that enlightenment? Cheers.
Destruction after last use (RAII) ensures that resources such as files, sockets, db connections are closed. Also unavailable in majority of languages.
Also the language is expressive (type inference, generics, pattern matching, traits).
These three things are the main reasons, although I wish other languages (old or new) would pick up the first idea too.
Also potentially if you want speed closer to C than interpreted script, and don't mind spending a bit more time up front. (offset by the time you don't spend debugging an inexplicable crash later on)
* Python has a GIL (Global Interpreter Lock) so writing multi-threaded Python code is more or less impossible (there are some workarounds but it still sucks).
* Most garbage collected languages (e.g. Java, C#) suffer from 'stop the world' pauses which make doing low-latency realtime stuff a pain in the arse. For example on Android they recommend you don't create any objects in your draw functions to avoid invoking the GC. It's kind of like "Java is great, no need to worry about memory, but oh... you really have to be careful about memory here." Not all GC languages are like this, e.g. Go now has sub-millisecond pauses.
And aside from that it is safer than C/C++. So basically it will be used wherever C++ was before.
The big downside is that it is definitely one of the hardest languages to understand and write. That can make you less productive. If I were on a varied team that included less skilled people I would definitely choose Go over Rust if it were suitable - it is a lot simpler.
For the increase in complexity, Go gives you concurrency, and Rust, safety, on top of other things.
It's very simple to write bad C. Writing safe, correct C with no security vulnerabilities is surprisingly hard, which is a major reason why most OSes need to apply regular security patches.
Rust gives you speed, concurrency and memory safety at the same time. This is not free: You need to know about values, references, stacks and heaps. And you need to write your code so that objects have a single, clear owner. It also helps to be somewhat familiar with generic types and functions like `map`—some old-school C programmers might have issues with parts of Rust, but C++ programmers (or C programmers who know JavaScript) should be fine.
If you're willing to pay that price, and spend a week or two making friends with the borrow checker, Rust is a fantastic and versatile tool. But for many programmers and applications, that price may be too high.
Rust will hopefully remain simpler than C++. But it's always going to be more complex than Go or Ruby. I have no idea whether Rust will wind up simpler or more complex than modern JavaScript, though. :-)
This is why I (ironically) offered to use Basic-80, which is even simpler than Go, replying to a text that suggests to choose Go over Rust for Go's simplicity.
Adding C++ to our toolchain would result in a nasty culture shock. Ruby and JavaScript have modern dependency managers (bundler and npm/yarn). They're easy to get working cross-platform. They almost never segfault.
C++ would bring a whole host of issues (build systems, dependency management, pointers, segfaults, etc.). I didn't want to go there, and neither did my management. But Rust feels more like a modern scripting language (except with a steeper learning curve): It has cargo to handle dependencies and builds, it works cross-platform with minimal fuss, and it provides excellent protection against shooting yourself in the foot.
It turns out that I can use Rust for CLI tools, server applications and Node.js add-ons, and it's great at all of these things.
Plus, every time I write something in Rust, my coworkers are happy, because it's fast, it's easy to install, and it's "solid."
It does take a little while to bring people up to speed on Rust. It really helps to pair program and explain what's going on when they hit a speed bump.
Ruby is easy to get working cross-platform? The only fully supported platforms are Linux and OS X (and really only the Linux-like parts of OS X). Ruby on Windows is a trainwreck, and other platforms are not supported at all. Meanwhile C++ runs on everything and the toaster. Ruby might have many strengths but cross-platform support is not among them.
(I still agree with you that it's not a super strong point.)
Compared to a C++ project which relies on a half-dozen external libraries each with their own build system? Yes, in my experience. I've spent too much of my life converting autoconf scripts to Visual C++ project files.
But Rust is definitely quite reasonable to get working on Windows, especially pure Rust, but small C extensions are OK as long as OpenSSL isn't involved. Usually I can just cross-compile or build using Travis CI.
rust seems like a great language, but i'd like to hear about some advantages versus golang.