Over the last three days, I've given my first full training on Rust to ensure there's now four people in Germany that can call themselves "trained Rust engineers". Thanks to everyone who attended and thanks to Linuxhotel for organising the training!
This was my first time giving a full training on Rust (we do provide training for Ruby and Elasticsearch). I'd like to note some of my findings down here. Also, the learning material is free (as in Creative Commons) for you to use, see below for more.
You can fully cover Rust as a language in detail in roughly three days. This includes all the language in basic fashion: borrowing and ownership, concurrency patterns, the toolchain and integration into existing applications through FFI or as a dynamic library.
Rust is not a simple language and should not be taught as such. It will only lead to frustration. My way of looking at it is that Rust has a lot of features that play well together, but are also interlocked, not layered. Also, it must be clarified that some Rust features may feel close to patterns already known before, but behave different in action then many other languages. Examples of this are traits reminding users of C++ Templates, or the use of
self as a first argument in methods reminding of Python. This is not a bad thing, but nothing that should be discovered by accident in the middle of a course.
Additionally, trainings rarely yield large programs. Rusts guarantees are not that impressive in small programs, a lot of them are greater in large applications. This means that some time should be given for theoretical discussion of those features, especially borrowing and "concurrency without fear", which help against many classes of bugs introduced when refactoring and extending large programs.
Cover the Basics
Three days won't yield you more then a basic overview of the language. I decided to not go deeper into the details of any libraries, e.g. Tokio or Futures, even though I consider them substantial to the future eco-system.
Explaining ownership and borrowing in deep can easily take up to two hours, with constant repetitions during trait explanations and similar.
The example I used was an extended version of the synced mailbox, a small connected FIFO queue. It nicely glues together many of Rusts features, namely ownership, borrowing, closureds, threads/sync, and optionally, traits and generics.
Have Flexible Course Material
Instead of having a strict path to follow, I prepared ~40 chapters focused on one topic each that can be easily jumped between. That allows to be flexible when the group runs into specific issues during tutorials. Many of the larger topics in the language (for example generics) were split into a "simple" and "advanced" part, so that they can be easily introduced at a glance when a question comes up and later handled in deep.
This was the best thing I found: hand people the docs early. People find their way around and use them. I had many questions during the example implementations but "where do I find that" was none of them. I could just point people to functions and concepts by name and they would find their way there. Thanks, docs team, you are doing great work!
Introducing Rust Feature by Feature
A great thing about Rust is how well all core features are integrated. But that also means they are hard to ignore for a while. The exact line one person had huge issues with was the signature of
fn splitn<'a, P>(&'a self, n: usize, pat: P) -> SplitN<'a, P> where P: Pattern<'a>
It has it all: lifetimes, generics, traits and bounds, a struct with a lifetime… All that while we were doing simple borrowing and string manipulation efforts. This isn't in itself bad, but as I hadn't introduced the lifetime syntax and traits before, that looked very different from the language I had just described.
A suggested remedy for this is a syntax introduction early on and telling people what to ignore in the beginning. The person was then very comfortable with working from the examples given in the docs.
I ended up explaining them okay, but lifetimes are an incredibly hard topic to explain in full. I still don't have a good, short example that makes two lifetimes necessary (
fn foo<'a, 'b: 'a>). The issues are the old ones: lifetimes are descriptive and nothing that "can be programmed with", which is an odd thing to interact with for many people.
The solution derived on the whiteboard was to explain how lifetimes are a mechanism to communicate a contract about pointers to the caller through the signature. With many developers not used to contract based development, lifetimes are still hard to get right on the first go.
Non-Editable Code in Slides
This is an odd one: the slides I created include code with a "run in Rust playground button". That lead to the wish to edit them to try out things on the examples. The more you give…
In the end, the general feedback about the course was that it works and gives a good overview of the language, especially in the finer problems that it solves!
Three days for a course is doable, but having some time more to play around and not push everything into an 8 hour day would have been good. On the other hand, it is a good trade off between time invested and learnings, as the full language overview can be given.
Teaching Rust has been a lot of
fn and people got what the language is intended for and what it is good at. People left with a better understanding of the language and the ecosystem. They also had a good grasp on where to go if they have a problem.
Material and Rerun
The material for the course is available under a permissive license on GitHub. Most of it is currently in german and is currently in the process of being translated to English. It's split into many small chapters, making translation easy. The translation setup is already done and in use.