
Using Rust for an Undergraduate OS Course - brson
http://rust-class.org/pages/using-rust-for-an-undergraduate-os-course.html
======
rdtsc
> Go. Go is a systems programming language

Not those kinds of "systems". It is funny, it seems initially it was meant to
be those kind of "systems", and then it pivoted as we like to say, to become a
"distributed-server-network-backend systems" not "hardware-kernel-OS" kind of
systems. And then creators kinds of winged it and remarked how "well, that's
what we meant when we said systems".

As for Rust, yeah, there are already a few projects trying to build a kernel
or drivers in Rust. Here is one for example:

[https://github.com/charliesome/rustboot](https://github.com/charliesome/rustboot)

~~~
zhemao
I think that is the definition of "systems" the class is using though. If you
look at the programming assignments, they are all about implementing programs
in userspace and not hacking on the kernel.

Yeah, I was confused at first too. Not sure how you can call it an "OS class"
if students never touch kernel code. Although, apparently one project group
did manage to write a kernel in rust based off of rustboot
([https://github.com/wbthomason/ironkernel](https://github.com/wbthomason/ironkernel)).

~~~
scott_s
It looks to be inspired by CMU's "Introduction to Computer Systems" course
([http://www.cs.cmu.edu/~213/](http://www.cs.cmu.edu/~213/)). The professors
who teach that course wrote a textbook, "Computer Systems: A Programmer's
Perspective"
([http://csapp.cs.cmu.edu/public/students.html](http://csapp.cs.cmu.edu/public/students.html)).

I was a TA for a year for a version of that course adapted to the school I did
my PhD at. We did the same thing: this course became the required one, and OS
became an elective. I was skeptical as well, but after TAing it for a year, I
think it is absolutely the correct thing to do. This course instills an
understanding of the whole stack of a computer system that exists in user-
land: assembly; how programs are executed including the stack, data segments,
dynamic memory; code generation; process management and concurrency; memory
allocation; network and server programming. The point of the course is how
different levels of the system interact, not just studying one component in
isolation. It is the kind of course I wish I had as an undergrad; instead, I
had to build that knowledge piecemeal during grad school.

More people will do user-land systems programming than kernel systems
programming, so I think it makes sense to make the user-land course required,
and the kernel level one an elective.

------
asveikau
I don't really get why this is a problem for a kernel programmer:

> lack of any intrinsic support for concurrency

An important part of an OS is that it _implements_ support for concurrency -
it builds processes and threads out of the raw materials such as page tables
and timer interrupts. A kernel also builds its own spinlocks and higher-level
waiting primitives. If you are relying on some high level language runtime to
do the heavy lifting, is that really learning about how an operating system
works?

For a kernel I actually see a lack of language support for concurrency as a
plus. You need something to sit below the higher level stuff.

~~~
pcwalton
Rust doesn't have any built-in concurrency in the usual sense either (well,
except for thread-locals and atomic intrinsics). What it provides is a few
simple features (uniqueness and safe references) that allow programs like
kernels to create their own safe, race-free abstractions. Instead of providing
features directly ("scenario solving"), Rust's concurrency features are more
like building blocks to help libraries build the features they need.

------
kps
Is there any support for the article's assertion that C uses ‘=’ for
assignment (and ‘==’ for equality) “to save memory”? dmr simply described it
as “a matter of taste”¹ and hinted that he was simply used to typing ‘=’ for
assignment². [Edit: And the compilers didn't keep source in memory, anyway.]

¹[http://cm.bell-labs.com/who/dmr/chist.html](http://cm.bell-
labs.com/who/dmr/chist.html) (also published in HOPL-II) ² _History of
Programming Languages II_ , Transcript of C Presentation, p692

(Nice to see Rust trying to bring progress to the C niche, BTW.)

~~~
comex
Not to mention that Rust uses = and == the same way as C, although the
expression "a = b" has unit type.

------
stephen_g
This just doesn't make a lot of sense to me, given that almost all systems
programming in the real world is done in C and C++, and given the complexity
of OS kernels, we're not going to see a general purpose commercial grade one
written in Rust either ever (if it falls out of popularity) or for ten to
fifteen years (if it becomes very popular). This isn't like application or web
programming - in comparison to web technologies it moves at a glacial pace. So
I think teaching an OS course in Rust is going to leave them underprepared and
be a big shock if the students actually go on to do systems programming on
real systems.

The other thing is that I did an OS course in C, where we got dirty playing
around in the depths of the Linux kernel, replacing the scheduler, writing a
filesystem, and so on. It was damn hard, but probably one of the only courses
in Uni that really pushed me as a programmer generally (everything else I
found easy). I think that it really helps to have experience in unsafe
languages like C, just like you really need to do at least a bit of assembly
when you teach computer organisation.

~~~
pcwalton
I'm not sure the lack of safety is the pedagogically interesting part though.
The interesting parts of writing a kernel for x86 are the low-level
interfaces: assembly, I/O ports, the 8259A PIC, the fundamentals of
scheduling, writing a slab allocator, and so forth. Rust allows unfettered
access to those just as C does.

The lack of safety in C just means that the projects take longer to debug
because the compiler doesn't catch as many mistakes; I don't think it adds
much to the learning experience.

------
vseloved
I'm also teaching an OS class, and IMHO using some new shiny thing is not the
right way to approach it, because the students will be put in an ideal
imaginary world, and neither will face the real-world practice, nor understand
how it all came to be the way it is now. I think, it may be a good follow-up
course (like "System Programming in Rust") after the "normal" C-based course.

I also don't agree with the notion that other option is "to teach students to
write C code riddled with security vulnerabilities, memory leaks, and race
conditions". This is exactly what such kind of course should teach them not to
do, clearly pointing these problems and explaining how to deal with them. But
not having exposure to that in the learning environment will only make them
repeat these mistakes in the real world.

Btw, I'm a Lisp programmer and not a C fan.

~~~
pjmlp
It is however a very good idea to teach them that C is not the only way to
write operating systems and it is currently on this position, because of a few
historical accidents.

Which we pay every day with extra tooling to cover for the language
deficiencies on the area of security.

------
wbthomason
I was one of the students in this course. If you're interested, I would
recommend reading the professor's "course wrap-up"([http://rust-
class.org/pages/course-wrapup.html](http://rust-class.org/pages/course-
wrapup.html)) - it discusses his philosophy going into the semester. In
particular, it addresses the most common comment I've seen in this thread,
namely the rationale behind choosing to focus on general systems coding rather
than kernel hacking.

Personally, I found the course enjoyable and worthwhile. It was not what I was
expecting, and I was admittedly disappointed that we didn't go much into
actual bare-metal programming. That said, I found it to be a good introduction
to the concepts of systems programming, with a few detours. As I am interested
in OS coding, I'm performing my own independent study to learn the material
which might have been covered in a more "traditional" OS course, but the
course did a solid job of covering concurrency, memory allocation, etc.

As for the choice of Rust: I really like the language, and plan to continue
using it for personal projects. I'm not sure it was quite mature enough for
exclusive use this past semester (the 0.7 to 0.8 switch midway through threw
the TAs for a bit of a loop), but I've been very impressed so far, and think
that this coming semester of the course (which will also use Rust) should have
a much better time of it. The one annoyance I had with Rust was the lack of
documentation, but the Rust community (including a fair number of those in
this thread) were incredibly helpful, and I'm currently working on a project
to provide an alternate tutorial, using a series of small "stepping-stone"
programs to build from "Hello, World" to a MapReduce implementation.
Personally, I think I might have picked either Go or a blend of C and Rust for
this past semester, but Rust is becoming a better choice as time proceeds.

I was also one of the members on the student team that made the ARM kernel in
Rust. Due to the timing of the project (right in the middle of preparation for
final exams and the rest of our end-of-semester project due dates) and some
"lazily evaluated" work ethic on the part of our team, it was fairly rushed,
and is much less complete and polished than I would like. I'm planning on
continuing work on it. Though I would disagree with the assertion that
separating Rust from its runtime is as "painless" as it has been said to be,
it wasn't terribly difficult, and solutions for that particular problem are
also constantly being improved.

------
zhemao
> its (C's) lack of any intrinsic support for concurrency

If you are writing the operating system kernel, who is going to give you
concurrency support? Does rust running on the bare metal still have "spawn"
and the other concurrency primitives?

~~~
pcwalton
They aren't language primitives; they're just part of the library. So
presumably you'd implement them yourself using a little bit of inline
assembler, and they'd then be on equal footing with the versions in the Rust
standard library.

------
dom96
> I considered five possibilities: Java, Python, D, Go, and Rust.

It's a pity that people rarely consider Nimrod (especially when it is far more
suitable than the first two possibilities). Here is an example for anyone that
is interested:
[https://github.com/dom96/nimkernel](https://github.com/dom96/nimkernel)

------
johnhamlet
"The Rust compiler normally disallows any code with race conditions"

Lol what? Unless tasks cannot communicate at all, or they are scheduled
entirely deterministically, how can the compiler eliminate race conditions?
How can a compiler even determine what is a race conditions and what is
intended non-determinstic behaviour.

~~~
dbaupp
The "default" communication primitive is message passing; and Rust has linear
types, so passing anything from one task to another will disallow the use of
that thing in the first task. e.g.

    
    
      let value = something();
      channel.send(value);
      // I can no longer use `value` here, it was moved into the .send call
    

(There is some subtlety here; some values don't move when passed around by
value, like primitive numeric types, but in general, anything that could cause
a race condition (i.e. pointers) moves.)

However, this isn't the end of the concurrency story; as mentioned in that
article there is the `unsafe {}` escape hatch which lets you do things that
could cause crashes/race conditions/etc. that the compiler wouldn't normally
allow (`unsafe` is basically the programmer saying "trust me, I know what I'm
doing" to the compiler).

This unsafe hatch allows one to implement shared memory with safe wrappers[1],
Arc (Atomic Reference Counted) for immutable shared memory (without any
locks[2]) and RWArc and MutexArc for lock-protected mutable shared memory.

[1]: [http://static.rust-
lang.org/doc/master/extra/arc/index.html](http://static.rust-
lang.org/doc/master/extra/arc/index.html)

[2]: Rust has very good immutability support, so it can express "this value
can never be modified" in the type system, so we can have shared memory
without locks entirely safely, because we know that there is no modification
possible and so no race conditions possible.

(I'm answering the data races question, which may be a non-sequitur if you're
asking about race conditions in general, as pcwalton points out.)

~~~
johnhamlet
Two tasks A and B both send a message to task C. Does the message from A or
the message from B get received by task C first? That's a race condition.

"A race condition or race hazard is the behavior of an electronic or software
system where the output is dependent on the sequence or timing of other
uncontrollable events."

"It becomes a bug when events do not happen in the order the programmer
intended."

[http://en.wikipedia.org/wiki/Race_condition](http://en.wikipedia.org/wiki/Race_condition)

If I thought that the message A would definitely get there first - who knows
why but that's what I thought when I wrote the code - this is a race condition
and a bug.

Please explain to me - how does the compiler prevent this race condition and
bug?

~~~
steveklabnik
From your link:

C++11 introduced formal support for multithreading, and defined a data race
strictly as a race condition between non-atomic variables. While race
conditions in general will continue to exist, a "data race" must be avoided by
the programmer, who must assure that only one thread at a time may access any
variable if the access is for writing.

~~~
johnhamlet
What does C++11's definitions have to do with anything? This is Rust.

------
nwg
Cool, but i think most people would not call this an OS course.

------
z3phyr
A similar kind of course for graphics programming is highly desirable.

Also, I might like to add, PS4 toolchain fully supports LLVM- Clang. It might
be wonderful if rustc starts running on it too...

------
2pxl
"I'm not aware of any other language that has concurrency constructs as
elegant and easy to use as Rust's spawn, and I don't know of any language that
comes close to the race-free safety guarantees provided by Rust."

I'd recommend having a look at Erlang, it allows you to start lightweight
pseudo processes for concurrency and provides a robust data sharing model.

Not so sure about low level access, we used C for those parts in the OS course
I took.

~~~
jerf
Erlang's a weird choice for an OS class. An Erlang program is run in a VM that
aggressively hides from you the exact things you want exposed in an OS class.
As a user, that's great, as a way of teaching OSes, it's not so great. Plus
the VM simulates a machine that is quite unlike today's real machines from an
OS perspective.

~~~
rdtsc
Granted for an OS class they just seem to be writting a lot of web and network
servers. Given their assignment list Go or Erlang would actually be a fit.

I for example remember writing a kernel driver and learning about virtual
memory and io schedulers. We wrote file servers in the "networking" class but
hey, that was years ago and a different college, so maybe I am just old.

~~~
tobiasu
Implementing Erlang on bare metal would be a good idea for an OS class,
writing code in it, not so much.

------
ijl
Can anyone elaborate on how "Rust changes the way you think"?

~~~
lifthrasiir
With Rust you need to be explicit about everything's lifetime. You cannot
allocate some chunk of memory and freely borrow it; when borrowing you need a
certificate (i.e. compiler check) that the borrowed reference has a shorter
lifetime than that of the referenced memory. It is tremendously different from
the absence of such guarantees (like C) or the GC-based weak guarantees (like
Java and Go, which does not solve the logical memory leaks anyway [1]).

[1] Not that Rust completely solves the logical memory leaks, but it makes the
leaks explicit.

~~~
oscargrouch
c++ smart pointers under the hood? its actually cool.. to bake some of this
directly in the language.. can prevent some memory copying by playing with raw
pointers indirections under the hood in the lang runtime, etc..

But you already has to think like this when you use smart pointers in C++..
"who owns this reference?" etc..

But in C++ this is a library.. not a syntax.. point to rust for this

~~~
sanxiyn
You already have to think like this when you use smart pointers in C++! But
C++ compilers don't help you with that! The Rust compiler does. Using Rust,
you get surprised how often you get ownership wrong, because the compiler
tells you every time. The same analogous code in C++ will still be wrong, but
you wouldn't know, since the compiler tells you zilch.

~~~
carterschonwald
hello sanxiyn! (fyi all, sanxiyn is one of the rust compiler developers,
unless i'm mixing up names :) )

~~~
dbaupp
(There are quite a few of us in this thread, fwiw: brson and pcwalton are
Mozilla employees working on Rust and Servo; sanxiyn, bjz_, steveklabnik and I
are volunteers, just from the handles I recognise.)

------
p0nce
For balance, here is an experience report teaching D:
[http://forum.dlang.org/post/yutwxozxoqrfalzfswht@forum.dlang...](http://forum.dlang.org/post/yutwxozxoqrfalzfswht@forum.dlang.org)

------
dave1629
I'm the instructor for the class (and author of the post). I'll try and
clarify a few things here.

If anyone is interested in participating in the next version of the course
(which starts Jan 14), please submit the form here: [http://rust-
class.org/pages/spring2014.html](http://rust-class.org/pages/spring2014.html)

\- "Operating systems" vs. "Systems programming"

If your definition of an operating systems course is a course where you
implement your own OS or hack on the Linux kernel, this wasn't an operating
systems course. But, most people have a broader interpretation of operating
systems courses today, to include courses where you learn about the layers
between high-level programs and physical things, and about how to build
robust, scalable, and secure computing systems. This is the second type of
course. Deciding do a course that was not entirely focused on building an OS
was not related to the choice of using Rust (I already knew I didn't want to
do a build-an-OS course before thinking about which language to use), and I
don't think its controversial (this is what the majority of top programs
already do). I discuss more about this in the general course wrap-up:
[http://rust-class.org/pages/course-wrapup.html](http://rust-
class.org/pages/course-wrapup.html)

\- Eliminating "Race Conditions"

You are right in pointing out that Rust doesn't eliminate all race conditions
(and no language that allows multiple threads and any interaction with the the
external world really could do this). My wording here was very careless. What
Rust does is use language/compiler mechanisms to eliminate the kinds of
pernicious data races (multiple threads reading and writing the same mutable
state in uncontrolled ways) that are a very common and hard to find and fix
problems in most multi-threaded programs.

\- C's assignment operator

My perhaps somewhat hyperbolic diatribe about C using ‘=‘ for assignment is
meant to illustrate how design decisions C's designers made for good reasons
given the computing systems they were using in the 1960/1970s, would not be
the best decisions if one was designing a language from scratch today. I think
the choice of ‘=‘ illustrates this well, but there are dozens of other more
serious issues in C's design that were good or necessary choices in 1972, but
are undesirable legacies today: not having bounds checking, not specifying the
order of evaluation for many constructs, allowing arbitrary and unchecked type
casting, unsafe memory management, etc. Other languages (including Rust) that
are strongly influenced by C syntax have also adopted the ‘=‘ symbol for
assignment, but that doesn’t make it a good thing. For languages like Rust
that are targeting experienced programmers it is probably the right choice;
for languages like Python that are intended as first languages, it is really
unfortunate, and many smart people who might otherwise turn out to be talented
programmers are unnecessarily put-off by this. (I don't have concrete data to
support this, but from having over 350,000 students in my open intro CS course
that uses Python, I have plenty of anecdotal experience with students being
confused by this.)

Vocational value

This was a course at a public university, so its content should not be
primarily driven by immediate vocational concerns. There's nothing wrong with
vocational courses, where the primary goal of the course is to improve the
immediate job prospects of students who take it, but was not and should not be
the goal of courses at public universities. (That said, the students entering
this class are already very well qualified for the job market, and many of the
4th years in the class already have job offers, so are not in danger of not
being able to get an interesting job since they only have C experience from
one previous course. Knowing Rust has actually been helpful for some students
in the job market, and I don't think having less C experience is a major issue
for many positions.)

From a purely pragmatic viewpoint, the majority of the costs of the course are
not covered from student’s tuition, but are paid by the US and Virginia
taxpayers. So, our main goal should be to do what we can to enable and
encourage students to do things that make the world a better place (the best
ways to do that, of course, are debatable, and maybe it is learning how to
hack the Linux kernel, but I think that’s a much tougher argument to make.)

------
axaxs
It's a good start, but I'd honestly feel cheated if given such a course. I
agree that the C tide may need to change, but using a language pre-
stabilization AND that has literally no job opportunities is a real drag. So,
the same goes for Go as well. And the fact that python or any other such
language was even an option scared me just a little. I believe in Rust, and
hope it succeeds, but don't feel this was a good avenue for it at this time.

------
rha16
Seems like a fun course. Just not an OS course.

------
UbuntuJon
Why wasn't PHP on the shortlist?

