Learn Rust as first language

Does anyone have blogposts or pappers of how is a good approach or how to teach Rust as first language?

I highly recommend the second edition of the Rust Book Foreword - The Rust Programming Language

4 Likes

If I wanted to teach Rust as a first language, then I'd first decide if I could teach programming in Rust without teaching mutability, or certainly without mutable references. I'd do this partially by looking at resources for teaching Haskell as a first language like Is Haskell suitable as a first language? - Stack Overflow but also by considering what I needed from the rust standard library.

If you can avoid mutable referenced, then you can avoid borrowing and lifetimes. If you can avoid lifetimes, then you can avoid all type parameters, and avoid traits as well. At that point you have largely achieved an introduction to programming course.

1 Like

I'm not sure of any materials. In my opinion, Rust isn't a good candidate for a first language. When starting out, most people struggle with simple operators and control flow logic. Adding lifetimes, ownership, and strict typing to the mix will most likely frustrate newcomers.

If you are teaching yourself, and you are prepared to take on the effort, the second edition book mentioned above is a good starting point.

1 Like

I don't find that convincing. Haskell is frequently tought as a new language - people grok all these things. Ownership and Borrowing are much better at communicating memory problems then raw pointers. Lifetimes can be pushed pretty far back.

I'd like to see hard data on such statements.

1 Like

I would disagree that Haskell is a good first language, either. I don't really get the point if one is just teaching basic programming topics. IMO unmanaged languages are just not a good choice for learning, and I'd much rather use more abstracted languages like Python and Ruby. Even the installation process (especially python) is very easy for Windows users (the OS most new to programming are comfortable with).

It should be said that I teach kids programming part-time, so my outlook is influenced by that demographic. If I had these kids writing Rust or Haskell instead of something like Python I guarantee many would get too frustrated and give up. The purpose of a first language is to introduce concepts and theory that can translate between any modern language. As an example, Rust's string slices vs String types can be very confusing to newcomers, and their usage is unavoidable. Even the first "hello world" exercise on exercism.io uses it along with lifetimes.

This is just my opinion and I don't have hard data on the topic. Certainly Rust as a first language is do-able, and if anyone is that determined, then go for it. However, I've found materials to be limited as it's usually not chosen as a beginner's language. Maybe that should change!

4 Likes

Disclaimer: The following sketch is just off the top of my head after a few hours thought. It's been many years since I myself taught beginner programming classes. However, I've recently struggled to grok Rust, which without doubt has been the conceptually-most-difficult high-level computer language for me to learn of the dozens that I've studied during the last half-century. In consequence, I may have stated some of the following details incorrectly; they reflect my current understanding of Rust, including after much study of the compiler source code as well as that of various other crates. (They also reflect my lack of sleep.)

Added: A few edits were added after overnight consideration.

Outline of possible course plan for newbie programmers
It might be reasonable to teach a carefully-orchestrated subset of Rust that exposes concepts sequentially. Ownership and temporary sharing of read-access vs delegating modify-access shouldn't be that difficult to teach, via analogy to physical objects in the student's experience.

I would start with simple scalar constant and variable declarations, scalar assignment, decimal numeric literals, and arithmetic expressions on scalars. I would introduce mut on the left side of declarations as a means of declaring that the variable's associated value could change over its lifetime. I would introduce the concept of ownership, along with the right-hand-side & prefix as the means to state that the value of the read-accessed variable needs to remain accessible for future use.

Scope and lexical accessibility-lifetimes are related; of the two I think that scope may be harder to teach. I would skip NLL for the moment (though NLL is really just a control-flow-based refinement of accessibility constraints, giving a more-precise range of statements where either shared-read-access or delegated-modify-access is permitted).

Teaching simple match patterns, even with conditionals (guards), should be straightforward. Boolean literals, variables and conditionals should be introduced at the same time.

Then I would introduce testing, giving just enough of the module framework and compiler meta-information declarations needed for testing. To support testing, a minimal description of println!() would be introduced at this time, with a promise that a detailed explanation would come later in the course.

At this point every successive topic would be followed by an exercise that employs testing.

Loops should not be too difficult. Simple iterators should be presented with loops, generally without iterator combinators. I would address break in loops and patterns at that time. To support testable examples, I would introduce an array of integers at the same time, along with the concept of indexing into an array.

Added: The Status enum, inline .method() invocation for unwrap(), and if let would be introduced with iterators.

Next I would introduce functions with variable arguments, omitting arguments that are functions (including closures). I would add the & and &mut argument signature prefixes as the means to state that the argument a) requires shared read access, or b) is being taken temporarily, respectively.

I would also introduce the return expression from within conditionals, including conditionals within loops, and use it within the exercises.

I definitely would defer complex type signatures, traits, compiler meta-information and macros, as well as floating-point, wrapping arithmetic, and to the extent possible representation issues such as signed vs. unsigned integers and selecting among the various ranges of integers.

Next I would introduce tuples, structs, arrays, vectors, and slices, along with general indexing and/or structure field access into all of those. The associated exercises might be to build a simple contact record as a struct of strings, and then a simple contact list as a fixed-capacity Vec(struct(…); N), similar to the contact list found on a student's smartphone.

The previous topic permits introduction of stack vs. heap allocation, so it's time to introduce smart pointers (e.g., Vec) and the idea of memory relocation of heap-based structures. The prior exercise would be changed to use a dynamically-sized vector.

I would next introduce UTF-8 character strings, discussing why direct indexing was inappropriate for items that were each of self-defined size of one to four bytes.

At this point I would add Rust's enums and have the students define different enum values for each field in the contact list, then have them reprogram the contact-list structure so that each contact was a Vec of assorted field enums. The associated contained values of some of those field enums would be strings while others would select from subsidiary enum values (e.g., "home" vs "work vs "mobile" phone classification).

At this point it is appropriate for the students to build a contact-list retrieval and printing application. The class discussion should include the advantages and disadvantages of each contact consisting of an unordered (vector) collection of enums and their associated data vs. a fixed field structure.

Added: Some point during the contact-list exercises seems an appropriate time to introduce lifetimes of objects on the heap. The unordered Vec of field-named enums might serve as the point of entry into that topic.

The above outline / suggestions are offered for what they're worth. No flames, please.

4 Likes

If that data point can be of any use, I learned programming via Delphi (which was, at the time, Pascal-based, so unmanaged) just fine as a 10 years old kid.

I think that when learning to program at a young age, something more important than the managed vs unmanaged aspect is the "instant visual gratification" / "something cool to show friends" aspect. Although not designed specifically for teaching kids, Delphi got this right by letting me immediately and very easily build simple GUI applications like I was used to see all around, learning the programming logic behind it progressively. From what I gather, Scratch and the Logo-verse also use a variant of this strategy to great effect.

From adult learners, one data point which I often get is that it is very difficult to learn to program in a garbage-collected language, then learn a lower-level language after that. Basically, garbage collection lets the learner be sloppy by forming a very inaccurate mental model of how memory management works, and it causes some painful un-learning later on. This would be an argument for teaching unmanaged languages first, or at the same time as managed languages, not much later. In general, I think it is useful to teach programming in a language which is as rigorous as possible about programming concepts (like Pascal was for me) in order to prevent the formation of flawed understanding in learners early on.

From that perspective, I do not agree with the current trend towards teaching programming in very magical or permissive languages like Python, Javascript, or C (the later being, from personal experience, especially evil for teaching by allowing you to write a wrong program which seems to work), and would favor more predictable and rigorous languages like Ada or Pascal, with some "instant gratification" libs or IDE added to prevent students from getting frustrated by the steep learning curve of programming concepts.

3 Likes

@doomy Maybe my reply was a bit hasty there, too, but: In contrast to other communities, the Rust community is pretty quick at "na, no, let's be a second language instead". I don't believe that is necessary. Every language has warts, every language has complexity and most learners are able to deal with that.

The Haskell community is much more positive about that. They explain what you would learn Haskell for and not go into details why their pet wart is a problem. The payoffs are more interesting then the road.

I agree that there's currently no good material aimed at pure beginners. There's a lot of accessible material, though, much in contrast to other programming languages. Depending on your use-case, Rust might be a better language to learn then - for example - C. In its current state. But there's nothing fundamental about Rust that makes it a better or worse language for beginners then others.

I teach Rust and a lot of concepts that feel weird to experienced (systems) programmers are much less though to beginners. Borrowing and Ownership are frequently cited. They are not hard, but people don't understand the whole scope of that, which makes them uneasy. This is a much easier situation for people that don't understand anything at all, currently. String/str is an good example, you don't need to understand the reason why it is like that to start finding ways to make that less painful. It's a fallacy that we have to explain everything to beginners. I find the hello-world example for exercism.io a bit weird, as well :).

Teaching children definitely introduces a bias and I would probably also not pick Rust there. On the other hand, I believe there's no such thing as "picking the wrong first language".

Finally, this is my opinion as well. Programming language teaching is literally an unresearched topic and most ideas are scientifically unproven and that's kind of my point.

5 Likes