Exercises to Learn Rust
rust-exercises.com
rust-exercises.com
It was my experience that it worked really well.
Some people were speed-running it, others took their time to read the book references that came up once in a while.
These 100 exercises build on the same interactive project format as Rustlings, so I would assume they're both great.
I think it might be interesting to develop Rust-as-first-language teaching materials, but that's not what this is. Move assignment as your primary assignment semantic, borrowing as your metaphor rather than introducing the confusing idea of "addresses" (in Rust many things we can borrow don't have a meaningful address, but it's fine) and so on.
I found that when learning Rust, it approximated the mental model that I had already developed for programming. Move semantics and such just felt right. I had used around 10–15 other programming languages by then, but none were even remotely Rust-like.
I don't know why this is. It can't be that Rust is the objectively or even subjectively right way to think about programming. It has to be that its concepts were simply already intuitive to me, but I wonder why that is, when so many people struggle.
Perhaps it's because of neurodivergence, maybe the language just matches the way I think in general and that's why it made sense so quickly. I'd probably make a terrible teacher, because I just do not understand the struggles people have with Rust, and I can't just teach someone to think the same way I do.
Maybe when existing Rust users try to write material for new developers, they write in a way that, for lack of a better way of wording it, is only really accessible to their own neurotype. In other words, it doesn't really help the people that genuinely struggle with Rust's way of thinking, it primarily helps people who already have this way of thinking.
For example the life of objects is something Bjarne Stroustrup's early editions of his C++ book neglect, basically saying yeah objects come into existence and then they're later destroyed and it's only in the third edition once C++ has more powerful techniques for this stuff that suddenly it's important that C++ programmers care about this and there's IIRC a whole chapter of the book.
It is also said that although Rust looks like a semicolon language, like C or Java or something, it's actually not like those semicolon language at all, it's an ML, the syntax makes it more palatable for semicolon programmers to learn and looks more "serious" for a systems language.
As an ML, Rust gets a solid foundation in its type system. Rust has a type with no values, and a type with one value, Sum types and Product types, so we're on firm ground here, we can do type arithmetic. Languages like C++ struggle to have a type with one value†, and can't really do "no values" at all. It's like your system of arithmetic doesn't have zero. You can limp along, the Romans did, but it's an unenviable situation.
† The C++ language isn't really sure how to handle these types properly because its rules say they need at least one byte of storage - but that entire byte is just padding. It's not insurmountable but it's very silly.
Anyway, I think what you're feeling is more real than you've allowed for. This is concretely a better foundation, that's not an illusion. It's not perfect but the sense that this is how things should be makes sense compared to other popular languages.
I know that things like ownership are universal concepts. They are relevant to all languages, even those that don't have a borrow checker. People who do not think of ownership generally do not write good code. They may not be aware of the concept or what it's called, but their thought process contains some implementation of it, even if indirect or limited.
In C, you very often need to know who is responsible for freeing a value, or when it is safe for the one responsible to free it. Someone may think of it in different terms, but the end result is the same. Even in something like Java you still might need to know who is responsible for managing a value.
Because Rust includes these things, teaches us to think about these things, and is generally designed with these things in mind, it is absolutely a better foundation than something like C or C++. A lot of C/C++ software, I'd say most of it, sorely deserves to be rewritten in idiomatic Rust.
But, you know, it's the "right tool for the job" thing. Sometimes Rust isn't the best for a particular application. Just because it's better at what it does, doesn't mean it's also better at what it doesn't do, if that makes any sense.
Or on safety we should not write new codecs in general purpose languages, including Rust, because these languages necessarily (Rice's theorem) can not check the semantic constraints we want to deliver safe codecs. We should use WUFFS. WUFFS is also a hard language to learn and as a special purpose language it's not applicable to most problems people have, but it is inherently safe† and delivers extraordinary performance so that's the right choice for this particular work.
† In C++ bounds misses are Undefined Behaviour, likely a security disaster. In Rust bounds misses cause a panic, likely premature program exit. In WUFFS any code which can have a bounds miss isn't valid, you get a compiler diagnostic saying you wrote this wrong, fix it.
UB also exists in Rust, however it's only supposed to exist in unsafe code, and even within unsafe code, you still benefit from Rust's great RAII, move semantics, deterministic destructors, and so on. It's still UB to index past the bounds of a memory region (well... uhh, insert Stacked Borrows or Tree Borrows here, this gets much more complicated, but you get the idea) but you can only do this unchecked from unsafe code, otherwise it'll always be checked and will panic if you attempt an out of bounds access.
When unsafe code is a special delineated section, you're less likely to forget to be very careful.
Anyone know the story behind this?
(Slightly more seriously, the project to replace the borrow checker was called Polonius[1], so it wouldn't be the first Hamlet reference in Rust land.)
I haven’t seen that movie a lot, but that scene (and really the entire movie) has been seared into my consciousness for 30 years. It was just the exact thing I wanted at that point in my life.
So my n=1 study indicates it was a successful design. :)
To be fair, there is no particular backstory. I picked a hermit crab as the book logo since crabs are strongly associated with Rust due to Ferris, Rust's mascot. I then landed on that style (and the skull) because they looked sick and distinctive. The cover images of many technical books are incredibly dull these days.
[0] https://www.lpalmieri.com/posts/a-taste-of-pavex-rust-web-fr...
[1] https://blog.logrocket.com/using-pavex-rust-web-development/
Book’s name doesn’t lie , it’s literally the author taking you by the hand along the path of building a real application with real problems on a real production environment explaining everything from the basics of the language to how to keep it online.
The book is pretty opinionated (because well, since it guides you until production giving you basically all the code, choices are needed) but the author always takes the time to explain its choices and what the alternatives are.
I’m feeling like I want to come back to this book.
My first thought after spending 30 minutes with this that it could easily become my new no. 1 recommendation for rust onboarding, toppling the zero2prod book.
Then I figured out why this felt familiar, it's from the same author :D
Will definitely dive into it this weekend.
I went through it and hated it. It's very tedious and I felt like I learned nothing from following along.
I still think zero2prod is extremely beginner friendly, you don't need to know rust well to follow it. You can just dive into rust concepts as you read it.
One suggestion: where you talk about using AsRef<[T]>, might be worth suggesting IntoIter, where it makes sense? IIRC that's what std uses for functions that aim at being generic over data structures and iterators (I might be remembering wrong, though).
In any case let's not digress from the truth: amazing list.
You're thinking of IntoIterator, which is a trait for when you want an Iterator, maybe what you've got is already an iterator, but maybe it isn't yet and yet it could easily be transformed into one (like say an array), so in either case IntoIterator::into_iter() gets you an iterator instead of whatever you have now -- the thing you had before is consumed by this.
Another you may find uses for is ToString, which says this obviously could be a string and I want the string. Unlike IntoIterator this does not consume the thing you wanted a string for.
There are also a suite of generic traits for converting to another type, From, Into, TryFrom and TryInto. As the person consuming things, prefer TryInto if you're clear what should happen when you can't, or Into if you need it to always work. As the person who is implementing the traits, prefer to implement From or if you can't TryFrom. Rust automatically cascades the conversions in one direction, so if I implemented From<Goose> for Bird, and then you later write code which needs TryInto<Bird>, that'll work just fine with a Goose using the code I wrote, because Rust concludes how to use my conversion to make it work, automatically - Rust will even be able to see that your "What if I can't?" error handling code will never run, and optimise it out of production code since I provided From<Goose> so this cannot fail - its Error type is Infallible, an Empty type.
Context: I've been doing Java professionally for 10+ years, Python, JS a few years ago, C++ way earlier in my career (early 2000s).
Haven’t looked deeply at this one in particular but they tend to teach you patterns and approaches. That works for a lot of languages. Got into zig pretty fast for example. You can start writing go in 2 days and even if you write shit code at first, it will run.
But I can’t get productive in rust and that kills my enthusiasm.
For learning JS internals, I used this: https://johnresig.com/apps/learn/
I just went through rustlings, and then this, and found both quite good. Rustlings is a little more gradual, perhaps, but basically just lists book sections to read for each problem set. This has a small outline of concepts for each exercise and is less reading.
I’d recommend either or both…and I found them both to be pretty accessible. Rustling is a slightly broader survey of topics just due to the reading. This is maybe a little quicker to the meat of it.
I don’t think either will give you 100% fluency in the borrow checker, but both will get you close enough to flesh out your understanding afterwards, and to make picking through readings more efficient afterwards.
"When I learned about async programming, I realized that for my CLI-based project, I didn’t really need async. So, I removed all async code and the Tokio runtime, which significantly simplified the code."
This implies that you used async before learning it. Does async come along for the ride with "standard" constructs in Rust, such that you have to make an active effort to avoid it?
Forgive my ignorance about Rust if this is a dumb question.
My reading is that, since their technique was to learn something and then immediately apply it to their project, they began making it async before they realized it was better of without it. Seems like a good learning experience to me, I think this approach is good.
Later on I then realized that in my CLI app I’m not gaining anything from it as there is no need for any parallel prosessing. It was just making my code more complex.
Consider a simple Java method signature:
public static MyCustomFileReader open(FileHandle someFile) { ... }
Who is responsible for closing the file after you're done with reading it?
Does the returned reader close the file handle on close, or should I issue another close on the file handle afterwards?
Can I open multiple readers on the same file? The only way to know is to check the javadoc comment, if someone took time to write it. In Rust there are no doubts like that.You can also bypass some constraints, but need to be explicit about it.
To the creator, a sincere thank you!
I have to say, this seems like a far more robust set of Koans than I've seen elsewhere!
So I went through the Build your own Redis challenge by https://codecrafters.io. It was a a good excuse to get my hands dirty with all the different Rust concepts (with a bit of structure).
Because lots of people do AoC, especially in popular languages like Rust or C++, you can see other people's solutions in the AoC Reddit solutions thread each day, which are often inspiring -- you learn new idioms, library features, or even whole CS concepts.
I implemented this in a webapp, but it's really awkward to have to go through the browser back in the DAW. An executable would be more elegant.
PS1: Some DAWs allow for gamepad input, but it's usually limited, difficult to configure, and buggy (Reaper for example has a bug that was first raised in... 2009 and never fixed).
PS2: I had hoped LLMs would help... but boy do they not! ;-)
I hate to be stickler, but on the very first page ("Syntax"), right after explaining comments, the text says "In previous exercise, you saw the greeting function..." Unless I'm missing something, this is the very first intro to Rust, and so there are no prior exercises...
> The exercise for this section is located in exercises/01_intro/00_welcome
Sad part is that there just aren't that many jobs out there that use Rust, let alone for someone with extremely little technical experience.
https://www.reddit.com/r/rust/comments/1cixuzr/official_rrus...
See also this account on HN.
https://rosettacode.org/wiki/Rosetta_Code
Edit: it seems the "more..." label down the 1st list to see all examples is broken. To explore the full list use this link instead:
https://rosettacode.org/wiki/Category:Solutions_by_Programmi...
Exercism.io is not just limited to one language it has a track for most popular languages. I've used it a couple of times and can definitely recommend it. You get to compare your solution with other (top-rated) solutions and you also get mentorship at times (depending on availability I guess).
This shows why I wouldn't use Rust for anything. Seems like a lot of verbosity for gains that don't really apply whatsoever for the kind of work I do (web apps and APIs). You have to remember let/mut fine, but then ownership/borrowing, and finally this `into()` thing - yuck! Like washing your face with sandpaper.
(I have historically been very skeptical of Rust on the web, but by now, I find it pretty pleasant. I of course know Rust very well already though.)
Understatement of the week ;)
In the case of setters, we get rid of them at the end of that chapter by using the newtype pattern thus guaranteeing that field invariants can't be broken even if you can access them directly.
That said, C# and C++ most certainly have their fair share of issues.
Just do your thing and be good at it.
PS: There was this VSCode extension to do BI stuff in the IDE (quarylabs/quary) showed here on HN and once I saw the CLI core having "Rust-based" as a feature I just closed the tab. What does "Rust-based" even mean? The tool may have been good but Rust is not a feature.
Anyway. It is more verbose than something like Go or Python. I’m not sure it’s really so bad compared to most languages, but some of the benefits you get from the verbosity is that you gain a lot of control over how you use mutability and the borrow checker or how you work with memory in general. Things which are foreign to many developers today, but you shouldn’t compare a Rust implementation to a C#/Java/Python/Ruby/Node/Go api, you should compare it to a c/c++ api… which is where Rust is just so much “safer” for production.
We have Node APIs in Typescript, we have Python APIs with FastAPI, we have C# web APIs and we have a few Go APIs. We’re perfectly happy with those, well maybe not the fact that we use so many languages, but we’re happy with them. Where we use Rust is where we used to use C, and the benefits are massive. You get the same sort of performance, but you also get an environment where programmers with less experience working without garbage collection can do work we just couldn’t have them do with C.
It isn't as if C# vlatest wouldn't be able to take up the task, Bing, XBox game servers, and other large scale services are fully on .NET.
So when this stuff was built the high performance option was C++ and there is an obvious reason to avoid that if you can. Rust means you can have the excellent performance without the absurd foot guns. Whether you chase that depends on other strategic priorities.
Just like Rust can take advantage of the LLVM IR features used by clang, so does C# in regards to the MSIL used by C++/CLI.
Indeed, there have been historical influences from Midori, C++/CLI, CoreRT[0], .NET Native/UWP and work done that got merged into earlier releases of .NET Core (up until 3.1), in particular, Span<T> and compiler optimizations.
However, pretty much all work done since Core 3.1 is independent, rather than "migrating of features from". The last and only exception would be NativeAOT which still uses project RedHawk name in some places in the code that haven't been renamed, referring to Midori and .NET Native (because NativeAOT has started with a big chunk of the codebase from the latter). But NativeAOT aside, pretty much all .NET features are exposed through IL and surrounding metadata, making it possible for any language to target these (rather than being related to C++/CLI), even if initially designed with C# in mind.
As far as I'm aware, C++/CLI itself uses MSVC to compile C++ code and then just generates bindings and glue code with other .NET assemblies, embedding itself into final .NET assembly produced that would use it. It did not target CIL with C++, like that new project for targeting CLR with Rust does, but rather CTS which is a quite old concept at this point. It is also limited to Windows only[1].
So the more accurate comparison would be between projects that produce .NET assemblies containing IL and projects that produce LLVM bitcode files containing LLVM-IR.
[0] https://devblogs.microsoft.com/dotnet/performance-improvemen... search for CoreRT and this is pretty much the only reference you can find in terms of work migrated from earlier projects
[1] https://learn.microsoft.com/en-us/dotnet/core/porting/cpp-cl...
As a user I know C++/CLI, since it was initially released as Managed C++ in .NET 1, replaced by C++/CLI in .NET 2.0.
They have two ways of compiling code, fully managed, meaning pure MSIL, where the Assemblies are considered as safe as other .NET languages by the PE Verifier. In this way, some UB behaviours and not so sane stuff from C day's, is forbidden in C++/CLI and will trigger a compilation error.
Mixing in native code. In this way everything from C++ is allowed, the resulting Assembly will be a mix of MSIL and native code generated by the Visual C++ backend, and will fail verification as a safe Assembly, being only allowed in unsafe code contexts.
My reference to it, is because until the improvements started in C# 7, the only way to make use of specific CLR low level capabilities, was to either do Reflection.Emit(), or reach out to managed C++/CLI.
You are fully aware of this, but many keep forgetting MSIL was designed for C like languages as well, and just like the hyped WebAssembly has all the necessary features to take full advantage of it.
Quite debatable. My experience is different. I have ported some tiny amount of Go code to async Rust and it turned out to be simpler and shorter. Way less boilerplate related to cleaning up the resources - in Rust it was actually zero additional code thanks to RAII and really nice channel design, while Golang needed a lot of additional stuff like waitgroups or manual defers plus more channels to communicate obvious things which in Rust are simply passed by result of a future.
Rust also feels a lot more expressive than Go with functional collection transformation chains (map, reduce, filter, grouping etc) where in Go this is loops and ifs all the way down. Rust is very close to Python in this regard.
(Note: For the specific example of integer-to-float, Rust has an alternative built-in syntax, but `.into()` is the canonical way that also works in generic code.)
In my experience in Rust, the use of getters and setters is less common compared to some other languages like Java.
> cargo install --locked workshop-runner
I have no idea what this program / package manager is so I was going to throw my hands up and say great another mac-only tutorial, but apparently this is a package manager for rust, and also apparently it comes pre-installed on manjaro lol
edit:
I'm confused, on the home page I clicked "next" which takes me to this exercise: https://rust-exercises.com/01_intro/01_syntax but on that ostensibly "first" exercise they write:
> The previous task doesn't even qualify as an exercise, but it already exposed you to quite a bit of Rust syntax. Let's review the key bits!
And keep referring to some prior exercise, that I can't find anywhere. Is there an "exercise 0" hiding somewhere?
It's not hiding, they link to it.
>> You can find the exercises in the companion GitHub repository.
https://github.com/mainmatter/100-exercises-to-learn-rust
And then they tell you to complete the exercises for each section before continuing and tell you exactly where the first exercise is:
> The exercise for this section is located in exercises/01_intro/00_welcome
Read the section titled "Structure" for all of this information.