one researcher's thoughts on concurrent programming and how to improve it

2012-09-26

Rust (0): Index and Conclusion

This four-post series on Rust is intended to introduce you to the language, to teach you about Rust's cool language features, and to give a debriefing of what I contributed to it this summer.

These posts are targetted for an audience with some knowledge of programming language design principles. You should be lightly familiar with both systems programming languages such as C++ and with functional languages such as Haskell or ML, and preferably strongly skilled in at least one or the other domain.

Do feel free to skip ahead, if you're already familiar with parts of the language, or to bail out early, if you're not interested in an involved tour of concurrency primitives. All the same, I hope you get something out of some or all of these posts.

Primer - an introduction to the language's syntax, memory model, and concurrency model

Typesafe Shared State - an overview of the region system and a parallelism library that makes heavy use of it

Typesafe Shared Mutable State - using trickery with Rust's type system to achieve a completely safe interface for common concurrency idioms (my second project)

I'd like to close with an argument for why I think Rust is the "language of the future" for systems programming.

Rust's strong static type system relieves programmers from worrying about many types of errors they should never have to. NULL pointer crashes, memory management errors, surprising implicit type coercions, and dynamic cast exceptions don't exist anymore. Meanwhile, features like closures and higher-order functions (missing in C++ (until very recent versions)), algebraic datatypes and parametric polymorphism (both missing in Go), and traits (existential types; a combination of haskell-style typeclasses and OO-style interfaces) allow you to concisely express ideas that would otherwise involve a lot of legwork in certain "conventional" languages.

Unlike other functional languages, however, Rust has heavy focus on performance as well. Stack-allocated data lets you often avoid dynamic allocation overhead and garbage collection (even closures can sometimes be entirely on the stack). The region system and borrow checker allow for type-and-memory-safe aliasing of arbitrary data with no runtime overhead. Explicit copyability as part of the type system lets you be aware of when expensive copies might occur.

Finally (and this is the big one, for me), Rust's type system includes a concurrency-aware memory model. Forbidding unprotected shared state and using message-passing over pipes as the main communication mechansim means programmers no longer have to worry about data races, and is also friendly to massively-parallel applications where cache-line contention is a serious worry. The use of noncopyable types means the message-passing library can safely assume all communication will be one-to-one, which allows for a blazing fast implementation under the hood. Noncopyable types also give other strong guarantees, such as the safety of ARCs and the fact that two tasks cannot deadlock when communicating over a single pipe.

Hopefully I've gotten you excited about using Rust for safe + performant parallel programming (or maybe several months from now, when its features and syntax are more stable). And to the Rust community: Thanks, it's been a blast.

I'm not all that familiar with Go, myself, beyond what I mentioned in this post (plus the fact that it has shared mutable state between tasks, and that it has implicit integer coercions), so I'm not really qualified to make a comprehensive table.

I do think of them as targetting the same audience -- the large-scale-systems-development C++ and maybe Java audience -- but for different reasons: go's attractiveness lies in its simplicity and approachability (and less compile time, i understand?), while rust is by no means simple nor approachable but does offer immense amounts of power. in a sentence, rust has a long learning curve.

you might wanna look at something like this, which I found with a google search.

how can popularity of language be increased hugely?

a complicated question. on the optimistic hand, I'd say a great first step is just communicating about it (like I'm doing here!) -- get the message out to the public, make sure people understand why it's exciting. On a longer term (still being optimistic), projects like servo should serve to prove rust's mettle, to get people to believe it's a credible and worthwhile language.

I don't really see rust overtaking "monsters" like C++/Java; my best dreams for it see it being maybe as popular as haskell. I've no clue how one would go about supplanting the industry standard languages; I'm not much for programming language pedagogy.

The most obvious reason rust isn't taught in college is because rust isn't even a finished language yet! While I was working on it this summer, it was about once a week that the syntax changed and someone had to go through the codebase converting everything. Big pain for students.

Saying that a specific programming language is more "powerful" just because it has more programming features builtin to the actual syntax is just plain wrong.

What people dont realize about Go is that it removed all the bloat out of the language specification and moved alot of those things that are builtin to other languages defintions into its standard library instead.

Go is a very powerful language for as young as it is. (not being built on established projects like llvm, jvm).

For what its worth there is a go llvm frontend called llgo in development and alot of the same bitcode powers both languages. (specifically the split stacks feature Rust adopted from Ian Lance Taylor's implementation in the Gold linker and libgcc runtime thats called from llvm)

To be sure, good syntax does not a good language make. But I do think Rust has a powerful combination of language features, syntax or no.

What people dont realize about Go is that it removed all the bloat out of the language specification and moved alot of those things that are builtin to other languages defintions into its standard library instead.

I admit to not having looked at Go's libraries. If you don't mind my asking, how many of the language features I mentioned in the bulleted list does Go have some sort of library support for? I would be surprised if algebraic/disjunctive types and region pointers could be supported in a library in a way that gives both performance and compile-time verification.

I feel like the claim of no NULL pointers is often overstated. Sure, we don't get any NullPointerExceptions, but we get plenty of "option none" failures instead. You'll still have to write "if x != none" instead of "if x != null". It seems like the real advantage is that we have pointers that might be null (e.g. Option<@T>) and pointers that definitely cannot be null (e.g. @T). We can't protect the world from lazy programmers, but we can at least make them try a little harder.

In fact, it's even a compiler error if you don't handle both the Some and None case. The problem is, the None case is often just "fail", and writing a while match expression just to unpack an option is a lot of typing, so we end up using Option.get a lot. Being lazy about using these features means your program crashes, and the end-user experience isn't much different from that of a NullPointerException.

Yeah, option::get() and option::unwrap() are funny cases. Experienced functional programmers will get a sour taste in their mouth whenever they write it, and (if they're dutiful) write a comment at the callsite about why the rest of their code guarantees it won't fail. But a novice programmer might not be as disciplined -- if one came into the #rust channel and asked "How do I convert a Option to a T..??" it'd be super remiss of you to suggest get() instead of match.

And now that you've mentioned it, I worry about option::get() and option::unwrap(). I foresee a lot of not-quite-expert programmers, familiar with C++ and null pointer checks but not with functional programming idioms, coming to Rust and littering their code with calls to get(). It might be good to put glaring warnings and capital letters in their documentation.

Yes, it's absolutely important to make lazy programmers try harder to be lazy. :) All the same, I think this is just a library issue and not a language issue (i.e., the claim is *not* overstated!). Consider option::map<A,B>(val: Option<A>, blk: fn(A) -> B) -> Option<B> -- this is well within the "safe" realm of the option idiom.

(And yeah, yeah, unwrap() is a common idiom but only because our language doesn't have oneshot closures yet. I hope it becomes much less common.)

Thank you for the write up! I am a sophomore in university and I'm currently trying very hard to get into research, specifically with language and compiler design (and a focus on functional languages and parallelism). Do you have any more information on the Mozilla internship? I would love to hear about it. Cheers.

Have a look at the research internship listings, which I believe were posted just recently. (I got in to it myself by sending an email directly and by being friends with someone who'd interned the past year, so I'm afraid my personal experience isn't universally applicable.) Thanks, and good luck!

As a Go programmer, I'm very interested to see how this plays out. Go's combination of power and simplicity/readability/learnability versus Rust's focus on functional programming (which more and more people appreciate daily) and additional power/features/complexity.

(a) recognises trends in computing and builds features around them (performance-conscious, message passing concurrency), and also

(b) has a featureset that allows folks to automate & statically check things that used to take a lot of manual labour to debug (algebraic types e.g. option types instead of null pointers; typeclasses and polymorphism for abstraction; using higher-order functions to build generic interfaces e.g. vector iteration).

To me, this means it's the sort of language we should eventually be building big, scalable, maintainable systems in. Over the summer, a friend from Facebook was telling me about some high-performance cache system, and I kept thinking about how rust could be used both to provide the performance and to add some static guarantees to ease developers' lives. The friend wasn't convinced, because, to be sure, rust's data representation / optimisation / codegen is a work in progress, and definitely not close today to what you can get by hand using C/C++... but with sufficient work on the language it could be pretty close.

Compared to the established languages (SML, OCaml, Haskell), the second and third points (performance and concurrency model) are new.

I hadn't heard of either ATS or Disciple -- both look very cool.

Compared to disciple, it looks like Rust is a lot more focused on concurrency/parallelism. I couldn't seem to find anything about a concurrency model on disciple's site. They do have an effect system that looks very cool, which is something we've discussed occasionally for Rust's distant future.

ATS... also looks awesome, especially given that it has linear types (like Rust) and a theorem prover for program verification (unlike Rust). I'm having a hard time figuring out what it's concurrency model actually is, though -- the language doesn't seem designed around it, which makes me think either it's more like concurrent ML (implicit copying and immutability), or if it has pointers, might enable data races. At the very least, Rust is very forward with its emphasis on message-passing + no shared state.

A few other differences that struck me: ATS's concurrency comes directly from pthreads, while Rust includes its own userspace scheduler. It also seems like, when using manual memory management, you need to free stuff by hand (and then prove a theorem that you don't leak memory) -- compare this with Rust's unique pointers, which get automatically freed, which I imagine would be much less hassle.

Thanks for your ideas. You can also find the details on Affity Solutions, at the System Programming. The main object of the Affity Solutions is to provide quality web services and is among the few software development company in Nagpur.