 So, hello. I am Andrew Brinker, and as you said, my talk today is a tale of teaching Rust. So a little bit about me before we just get into stuff. I've been following Rust since like 0.8. I'll write the Rust frequently asked questions page, which I'm actually trying to update right now and add more questions to because it's been a little while. If you use that, you know, it makes me happy. And basically the background of how this talk came to be is that I was asked to teach a class on programming languages. Basically a programming language theory class for Cal State, San Bernardino, which is my alma mater. They basically had an extra section and they needed someone to cover it and asked me if I would do it. And I said, sure. Now, normally the class is done with, so it's four languages, usually Lisp, C, Java, and Prolog. And I decided, okay, well, let's replace the C with Rust because it's 2017 and the class has been around for decades. And C is fine, but I wanted to replace it with Rust because, you know, why not? But then there's the question of how are we actually going to do that. They're on a quarter system, so the classes are ten weeks long and you're covering four languages in ten weeks, which is kind of intense. So, you know, how do you actually fit Rust into that? And what stuff from Rust should you actually focus on? So, oh, other thing, I also replaced Lisp with Haskell, but I won't be getting into that. So, three weeks for Rust total, 3, 3, 2, 2, there is absolutely no time to teach a bunch of students Rust with, like, a couple hours a week across three weeks. So then how do you actually do it? Well, you have to figure out what is Rust's pitch, what is the, what are the things about Rust that other languages don't do, can't do, what are the interesting things where, if you're introducing Rust to someone brand new that you would focus on. So, probably all seen this list or something like it, this is basically a slightly modified version of the list you find on the Rust website homepage of just, like, here are the cool things about Rust. And so, from this list, I picked three things. Guaranteed memory safety without garbage collection, threads without data races and optional unsafety. So, that means week one is safety without garbage collection, which is basically ownership, borrowing, and lifetimes. Week two is threads without data races, so week, which is basically everything in week one, talking about send and sync and basically how concurrency and parallelism works and how Rust ensures safety there. And then week three kind of explain why all of it matters. So, what's the plan? That's sort of the brief, but then what's the actual plan? The class is a fairly small class, 26 upper division undergrads. These are like juniors and seniors. A whole bunch of backgrounds. Some of them have been programming since they were, like, in their cribs. Others have been programming for, like, no time at all. They, you know, just a year or two. None of them knew Rust going in, but some of them had heard that Rust was hard. Like, if they had heard about Rust, what they had heard is that Rust is hard and scary and they didn't want to try it. So, what's the approach? How do you actually get people who are, if, either not sure of or, you know, best iffy about Rust interested? So, the first thing for me was to be really concrete. There's a, this is definitely a mistake I made with Haskell was diving into the sort of abstract nonsense a little too quickly. You want to, I say this to someone who loves the abstract nonsense, it can be challenging for, for new people and for newer programmers. So, you want to be really concrete. Want to provide lots of examples, live coding. A lot of the class was actually structured as sort of a question and answer type thing. So, I would, I had some examples that I would prepare and then sort of work through them and the students had questions and sort of say, oh, let's find out and live code and work through the answers. Now, this requires that whoever is doing, if you're the teacher, requires you to know Rust well and be able to kind of roll with the punches. So, there's some things you can do to handle that. One is to do your examples beforehand. Like I said, I had like a collection of examples each week that I could work through and sort of go to that I felt would answer common questions. Also, Rust C can actually be a really fantastic teacher because of all of the work that has gone into error messages. They're actually really nice now and you can use them as a teaching aid, which is definitely something that I did. Just also a note, if you don't know something and you're trying to teach people, this is true of Rust or just anything else, don't fake it. Don't try to guess at why something is the way it is. Say, I don't know and come back to it because the worst thing you can do is give an incorrect answer that gives them a bad mental model or understanding of what's happening and then they have to sort of unlearn it later. It's also helpful to know the ecosystem of tools. Rust has a ton of fantastic tools that can be used for learning. This is sort of a collection of something that I used. The playground is great. Students really like that. The Rust Book, particularly the new edition of the Rust Book, has tons of fantastic stuff. I put a little heart next to rustlings because I really like those. If you're unfamiliar, that's basically they're a collection of short little programming problems in Rust that are collected in sort of topics to teach a particular idea. And then there's the Godbolt compiler. So that's basically a system that allows you to very easily look at the assembly output of various languages. It was traditionally with C or C++. It works with Rust now and that's super awesome. And then also you want to give the students room to explore with those tools. The class was definitely kind of free-form. Students were encouraged to play around and ask questions. And that gets us to the actual class. So lab one, safety without garbage collection. Start out, you get down what are the rules at hand here? So all data has an owner. Ownership can be moved around and you give examples for that. The old owner can't use the data and then you talk about the idea that simple types can be copied. This should all be sort of easy and known for anyone who's worked with Rust. You sort of work through this and you have them do increasingly complicated programming examples with it and they realize that if this is all you have, it stinks. Like if all you have is ownership and you can't do borrows or anything else, it's complete pain. Students realize it's painful to work around. And so then you introduce borrows. And so for borrows, this is the idea. I found the clearest way to explain it. Most students got was basically that you can either have aliasing or mutability but not both. Once again, emphasize the rules, give lots of examples and show complex cases. You want to really work through it and put in the effort to make sure that their understanding of it is solid before you move on because otherwise students can get really lost. And this is a thing I've seen with newer programmers. They try to not read the Rust book and they try to just jump in and start writing stuff. I'm sure you've all seen questions where people are like, I tried to write a doubly linked list in Rust and it doesn't work. Why does Rust stink? And the answer is well, okay, there's a lot of stuff you've got to do first because it's not as easy as you necessarily thought. Doing it in sort of this constructed, very structured way I found is really the way to go. And then students generally will ask, okay, how does this work? How is it actually verifying that everything I'm doing is safe? Because this still seems like magic a little bit. And so that's when you introduce lifetimes. And so actually this next one will no longer be true with non-lexical lifetimes. But obviously non-lexical lifetimes are not yet here. And so for my teaching, this is what I taught. Basically that lifetimes are scopes and that all the boroughs have them. So whether they're explicit or not, everything has one. It's there. You don't have to necessarily know about it. And the compiler is tracking them. It's not like when you make it explicit that it's suddenly born from the ether. It was there already. Also very clear, you can't assign a lifetime. You can't just arbitrarily say like this thing has this lifetime that is not its scope. Like no, that's not students would try to write code like that. And then there's the last, okay, well what's up with like, instructs that you have these these lifetimes are when you have functions that are generic over lifetimes. What's up with that? And so you talk about, okay, well, you can have code that's generic over lifetimes and your lifetimes can have constraints. And once again, it's very sort of structured lots of examples. Everything should be kind of building on each other. And that's when and you'd say that okay, when these situations, that's when you make lifetimes explicit. And if you have time, I didn't because once again, it was basically three weeks with Rust. And that's like class twice a week. It's I think it's a good idea to try to introduce cell and ref cell, just if for no other reason, then they shouldn't seem like magic. They can seem like magic sometimes. If you're not familiar with what they are, why they exist, how they work. And so I think introducing them is worthwhile. And so the assignment for this class was actually to do the move semantics, rustlings, exercises. This is a great thing. It was why I'm very happy this sort of stuff exists. Other people have done lots of work that I can just borrow for lessons, which is always nice. So the move semantics, rustlings, exercises are basically just a collection of these programming problems that guide you through and sort of test you on understanding these topics of ownership and borrowing and lifetimes. And so that's week one. Week two threads without data races. So basically, this is, you know, how does Rust achieve once again, this thing that can seem like magic. A lot of this really is demystifying the magic. So start with definitions. This is a thing people I found particularly with the new students who are new to computing in general, don't necessarily know all of the terminology of concurrent and parallel programming. And so you want to, you know, not going to read all these, but just sort of these sorts of definitions, things you want to get down. What are data races, race conditions, concurrency, parallelism, so on. Same as before, be super concrete. You definitely do not want to hand wave stuff. And also I think it's worthwhile to really talk about send and sync. Explain what they are and why they exist, why they are implemented for some types and not for others and talk about as well that they are automatically implemented in certain cases. But I will suggest not to introduce unsafe traits because we're going to get into unsafe in week three. Introduce the key standard types. Obviously these are not like a full list, but I think these are the ones that they're going to see and likely use. Talk about RC and ARC and UTech and read write lock. And this is, I think, crucial explain why ARC is sync, but RC isn't. Explain the idea that you can have types which are similar except for being thread safe or not. And then this is a big one and this gets confused even in like online conversations about rust sometimes. Make it clear that rust stops data races, not race conditions. I've seen so many times when people are like trying to pitch rust on some online forum and they say, oh yeah, rust stops race conditions. No, it does not. A rust is great and stopping data races is great, but it does not stop just race conditions in general and it's probably not good to tell people that it does because then they'll see that it doesn't and be disappointed. Also, rust has super cool stuff in this world with the easy parallelism things. These are some libraries that you can show off and be like, look at how easy this is to take a for loop and make it parallel or whatever it is you're wanting to do. These are fun things to show how good it is and justify why we like having safe and easy concurrency and parallelism. And then so the assignment was to solve the dining philosophers problem. If you're unfamiliar, basically it's a classic problem in parallel computing. You've got a table with a bunch of plates of food and forks and philosophers sitting at with each of the plates of food and the philosophers will only eat when they can pick up both forks and the forks are shared and you have to figure out some way to make sure that all the philosophers eat, no one starts to death. And it turns out that this is actually is challenging. The first edition of the Rust book basically walks through solving it. So basically what I did is I took that code, modified it a little bit and then took parts out. And so I handed them basically partially written code and said, you know, finish it. And I think this is useful for a few reasons including that it really requires you to sort of clarify the difference between something compiling and something being correct. About a third of the students initially turned in work that compiled but that modified the problem in some way. Some students just added more forks. They're just like, yeah, the solution clearly is just we get rid of, like get rid of the contention. Just everyone has a fork and then it's solved. I think my favorite one was they just was one student who basically put every philosopher at their own table. And they turned that in. I'm like, okay, great. I'm glad you got this to compile. And that's, you know, great. But this is not the problem. And you have to kind of walk through that a little bit. And so then that takes you into week three, safety and security. So this is kind of a bold move. Third week of them working with Rust et al introduce unsafe. This may seem crazy. So first you want to explain the notion of safe rust and unsafe rust and almost that they are not different languages but that you have to sort of view them distinctly. I think it's also useful to talk about unsafety in this split context. You have unsafe functions and unsafe traits, which are basically things that have requirements the compiler can't check. And the programmer basically passed to check them and said programmer being the user of the functions or the traits. And so that's basically establishing like, hey, there's some requirement here. And then you have unsafe blocks and unsafe trait implementations, which are the other side of it. Basically you're telling the compiler, hey, I've checked these. And the compiler says, okay. And just goes from there. Safe rust has to trust unsafe rust. This is a point which is made very heavily in the advanced Rust programming language book also called the Rust and Nama Khan. Basically, safe rust obviously is more limited in capabilities than unsafe rust. And so if unsafe rust is wrong, safe rust can't work around it. And so I think it's useful to make that idea clear. On the flip side, unsafe rust can never trust safe rust. I think the example that the Rust and Nama Khan gives is of a piece of unsafe code that uses the ORD implementation of some type. So it's implementation of the ordering trait. And basically relies on that ordering being sensible. But you can't do that because the ORD is a safe trait and it should never be the case that safe code from another module outside the boundary can induce unsafety. And this of course then connects to the idea. Safety lies at the module boundary and you want to make that clear and that's really just a consequence of Rust's privacy rules. Now the reason I sort of think it's useful to bring all this up, I work in cybersecurity as like my day job and safety has an impact on security. And there are a lot of context right now. So you have like C or C++ which don't necessarily provide the same safety mechanisms and they're used in environments that obviously are maybe embedded, can't tolerate garbage collection. You have different places where these languages are still used. We have lots of old existing code that's in C or C++. And we have a lot of these common sorts of vulnerabilities that we've known about for a long time that still get written and discovered and cause lots of problems. And so the assignment for the students for this week was actually to find a CVE that could have been stopped by Rust. So if you're not familiar with the CVE list, the common vulnerabilities and exposures list, it's basically a disclosure list for vulnerabilities in common and popular software. And so I basically gave them the definition that Rust uses for what is considered unsafe behavior and said find something in this list and then find a vulnerability in the CVE list that is of this type, right? So a buffer overflow, maybe if you talk about Rust dealing with panicking on buffer overflows or if you want to talk about use after free and some CVE that was related to a use after free error. And so I basically set them loose on the CVE list and said come back to me with a CVE and a little explanation of what's going on and how Rust would have stopped it. And this I think connects to this idea. There's been a suggestion I guess that Rust should in terms of marketing or like selling Rust move a little bit away from pushing safety because lots of people aren't interested. And I totally get that perspective but my feeling really is that the safety is the big deal. The fact that we can have this code you can write code that works in the context where C and C++ are used but that doesn't have the same sorts of problems. I think that's a really big deal and I wanted to sort of illustrate why you should care about safety. So what were the results of this grand experiment? Well I ended the series with a post Rust survey and every student participated and here's basically what they said. So students thought that the compiler was too picky. Yeah that should be surprising to no one. The compiler can be a real pain. You know you solve one problem and another problem pops up and it turns out that you're thinking about the whole thing wrong and have to do some big restructuring as you sort of get used to the way that Rust requires you to think. Also the syntax was weird. Students were mostly used to C or C++ and Rust is sort of this hybrid of C and C++ but also like taking things from the ML world. The original version of the Rust compiler was written in OCaml and obviously Rust's type system takes a lot from Haskell and syntactically it just sort of is drawn from lots of different places and they're just like okay this is weird and uncomfortable and I don't like it. This one was funny to me. A couple said they only liked Rust in comparison to Haskell because Haskell was crazy ridiculous, abstract nonsense and Rust was like slightly less ridiculous abstract nonsense. But don't despair. There were good things they said to. Most of the students wanted to try Rust more after it was done. There's only three weeks, not a ton of time but a number of students indicated some interest or preference to continue working in Rust. Students also felt that Rust was really powerful. I think this is the really great thing I know this someone gets talked about a lot that Rust can sort of bring people into these domains where they wouldn't otherwise be able to really operate because maybe just because of intimidation or because of sort of how rough around the edges working in like low level C and C plus plus can be and that Rust safety mechanisms provide sort of a freedom to experiment that you may not get with C or C plus plus. By the same token students felt more confident with Rust than they did with C plus plus. And that was something that I was certainly very happy with. And I think that there are even more reasons for optimism. The ergonomics initiative will really help new Rustations. A lot of the stuff that was discussed actually just in the last talk about the ergonomic improvements being made to the language directly address a lot of the pain points that my students came to me with. And so that's really encouraging. I think that that will make introducing Rust to new programmers, introducing Rust in general, I think just a lot better. Also this one actually surprised me students did a lot better with ownership, borrowing and lifetimes than I expected. And I wonder if this is just because they most of them were newer programmers. I think that when you're a more experienced programmer, let's say you're coming from the world of like professional C plus plus development. Rust can seem very weird and different and you're like there's all these new rules and learning them requires a bit of unlearning of other things that you already know. But if you're newer and you don't have things to unlearn yet, they actually did better than I was expecting. So in conclusion, can Rust be taught in three weeks? No. But you can get students excited and interested and get them wanting to try it some more. Can Rust be taught to newer programmers? Absolutely. I was really pleased with how they did and with the ease with which they approached some things which I really expected where they were going to find harder than they did. And can Rust overcome the bad press on its learning curve? I think so. Remember, most of these students came, most, but a number of these students came in having only heard that Rust was hard and scary and thinking basically that they didn't want to do it. And by the end, those students were pretty much universally either like okay on Rust, like with Rust, or excited and interested in programming in Rust more. So thank you very much.