[ intro slide - my language background ] Hi. This talk is going to be, mostly, a plug for Rust. Those of you who know me may find this surprising. After all, Rust in its current form is only about 4 years old, and I myself only learned it in December. I'm not known for liking new things :-). All I can say is that I tried it and have been impressed. In most respects Rust is a competent but not particularly novel language. It has one novel feature, but the rest feels like a collation of good and established features from other competent languages. Rust is most often seen as a competitor to C and C++. That's fair as far as it goes: certainly there are few reasons to use C or C++ over Rust for an entirely new project. But for me, Rust's power and flexibility mean its appeal is much wider than that. B======================================================================B [ MM survey slide ] Firstly, I'm going to go into some detail about Rust's one very novel property: it's approach to memory management. Let us consider, particularly, dynamically allocated memory. Until now there have been basically two approaches: In lower-level languages like C, C++ and assembler, you as the programmer must know whenever memory allocation is occurring, and control when that memory is freed, usually with explicit code. Mistakes lead to bugs - often, bugs which are very hard to track down. There are some static checkers, and coding conventions, which help to reduce the number of these bugs but they are still a big problem even with the most careful software development practices. In higher-level languages, memory allocation is often implicit and deallocation is handled automatically by a garbage collector. This is convenient for the programmer. But it requires support from a language runtime, which is awkward in some environments and makes it hard to interface to other languages. And it costs performance - sometimes, a lot of performance. Traditionally, garbage-collected languages have focused on programmer convenience, or programming language power, rather than performance. [ MM survey slide + Rust ] Rust has a different approach. Each object in a Rust program has exactly one owner; for a local variable that owner is the variable. For an object in a complex data structure, the data structure is the owner (and of course that data structure has, in turn, an owner). In many ways this resembles informal notions of object ownership which programmers have typically used in C and C++ to make their code comprehensible. [ Borrowing example slide ] https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html#mutable-references But in Rust the ownership is known to the language, and the rules surrounding ownership are enforced by the compiler. When an object ceases to have an owner, it is freed. This ownership system is used not only for dynamically allocated objects, but for local variables, and statically allocated objects. [ dangling reference error example https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html#dangling-references ] To allow an object to be accessed other than through owner, Rust formalises the notion of borrowing. An object can be borrowed by writing the ampersand, giving a reference. The compiler checks that the object will outlive the reference. [ Borrowing example slide with mut missing ] ABOVE https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html#mutable-references Borrows (that is, references) come in two kinds: mutable and immutable. You can have many immutable references at once. A mutable reference permits modification, and provides exclusive access. That's what the `mut' is doing in that example. If you leave it out the compiler complains. This is the Rust `borrow checker' that you have may have heard about: the part of the compiler which enforces the object ownership rules. Overall, this scheme makes use-after-free and double-free bugs impossible. You have to work at it to make a memory leak. Rust programs are safe (by default): you can write bugs, but you cannot randomly corrupt memory (or otherwise trigger what in C/C++ is called "undefined behaviour"). The ownership rules even give you safe multithreading. And, the ownership system means that the compiler can often optimise very aggressively, because it has really good visibility of all the aliases of and references to the objects its working with. Compared to garbage collected languages, the ownership system eliminates a lot of runtime memory management. I have found Rust programs to generally be very fast. C======================================================================C [ tour - syntax ] Apart from the ownership system, there is little new in Rust. Nevertheless, it is an advanced language with a lot of expressive power - power which is generally available without sacrificing performance. Despite being an advanced and powerful language, Rust mostly manages to feel quite familiar. I have found it easy to learn and to use. I'm going to zoom through a few of Rust's most important properties: Rust's syntax is a conventional structure of curly braces, keywords, parentheses, and infix expressions. It looks a lot like C or JavaScript or something. [ tour - type annotations ] Rust is statically typed. The compiler will typecheck it. This is great. You may have heard Haskell and Ocaml programmers say "once you can get the program to typecheck, it will probably work". Rust has the same experience. When in the throes of writing a complex algorithm you can type some drivelous pseudocode into your text editor. Then keep fixing errors until it builds and lo! it will often work. [ tour - type inference & polymorphism ] Rust also has type inference (similar to Ocaml, Haskell, etc.), so you can often leave out the type annotations. Rust supports polymorphism (also known as `generics'; it calls the feature `traits'. They're a bit bit like C++ templates, but not mad. Rust supports dynamic dispatch (like `virtual' in C++), or static dispatch, but in both cases the typechecking is done at compile time. [ tour - safety ] Rust is safe by default. That is, bugs in your code can't corrupt memory the way that they do in C and C++. But, unlike most other safe languages, if you really want full manual control, you can write `unsafe'. This is rarely needed, even if you want really fast code. [ tour - unsafe Rust/C/C++ example, chrobakpayne.rs glue.cpp ] Rust has a reasonably good system for interfacing to code written in other languages. With the appropriate annotations, you can call C functions directly from Rust and vice versa. (Of course this requires you to say `unsafe', since the semantics of the C parts of the program are not known to the Rust compiler so you must check them yourself.) Built on top of that are machineries for glueing Rust to, for example, Python. D======================================================================D I also want to talk a bit about the attitudes and values of the Rust project. (I should say that this is an outsider's point of view.) [ slide with quotes from C vs Rust ] The most impressive thing, for me, is that the Rust community does not blame Rust programmers for the bugs in Rust programs. Rather, they look for opportunities to help the programmer avoid bugs. There is a clear design ethos, to make APIs where the default and easy option does the right thing, and to avoid beartraps. In particular, the comparison with C is very striking. The C community have even resorted to writing compilers which ferociously analyse your program for violations of C's bizarre and nigh-incomprehensible rules, and then use those rule breaches as excuses to miscompile your program. Convenience is not regarded as the enemy of correctness, but its ally. [ slide with interface stability ] Since declaring 1.0 in 2015, the Rust project has been quite careful about language and library stability. They make a clear distinction between stable and unstable features, and try quite hard to avoid backward-incompatible changes to anything which has been declared as stable. [ slide with error message ] The Rust compiler's error messages are truly outstanding. They are brief enough to be comprehensible, but contain enough information to know precisely what is wrong. They even usually come with a suggestion from the compiler about what to do about it - suggestions which are right about half the time. [ process and code of conduct ] There's a mature process for evaluating and deciding on new features, and a modern Code of Conduct. E======================================================================E Of course nothing is perfect. I'm very critical by nature, so I often find things to gripe about. [ borrow checker error, lifetime example ] The most obvious difficulty with Rust, if you read the internet, is some's feeling they are constantly fighting the borrow checker. Personally I have not found this to be a problem at all. I think it may depend on how much experience of informal object ownership systems you have - in particular, how much C and C++ you've done. Of course having something like the borrow checker is the price Rust pays for its novel memory management strategy, and it is that novel strategy that makes for the high performance of Rust programs. There is one annoyance that occurs when trying to design and implement complex APIs. It is not straightforward to define an object type one of whose members is (or contains) a reference to another member. Luckily this doesn't come up very often, and it is still possible to hide the issue from the API's consumer. The awkwardness is tolerable. [ serde | macro_rules! ] Rust does have macros. In fact it has two ways to define macros (one built on top of the other). The more sophisticated macro system is very capable and has been used to really impressive effect. You can see an example here. But it's hard to use casually; and the simpler macro definition facility is simultaneously too complicated and underpowered. So casual macro use is slightly awkward. [ cargo - dw posts, links ] But the worst problem is cargo. Cargo is Rust's language-specific package manager and build tool. It is really quite annoying. The worst problem is that like all modern tools of this kind, its main purpose is to automatically download stuff from the internet and run it. Apparently this is what is expected by the youth of today. It can be hit hard enough to stop it doing this, but you still run into the difficulty that if you use one library from the repository, you end up having to trust much more widely than ideal. At least Rust's repository contains larger libraries than, say node.js's, so the number of people you're trusting is much lower, but it's still troublesome. Also, as a build tool, cargo can be very inflexible. Despite an official policy that cargo should be suitable for running inside another build systme, requests to be able to do `weird stuff' seem usually to result in excuses why the thing you want to do is a bad idea, rather than useful increases in flexibility. (And the excuses are usually based on misunderstandings.) I guess if your project ends up containing piece of opinionated curl-pipe-bash-ware, you should expect a culture which produces these kind of problems. [ other problems ] Rust does not currently readily support dynamic linking between Rust modules. This is something the Rust community know they need to work on but it hasn't been their priority. Some of Rusts's language design choices make this challenging, although not impossible. In particular the very nice system of generics. Personally I like the tradeoff that Rust has made here. I expect that some form of dynamic linking or partial compilation will very likely appear eventually. But in the meantime there are places where replacing C with Rust is awkward. Rust depends on LLVM. So its architecture support for smaller architectures (with less money behind them) is poor. From my point of view as a Debian contributor a particular problem is the lack of an m68k backend for LLVM. But it provides a more general difficulty with supporting a wide set of embedded architectures, which Rust would otherwise be very suited to. F======================================================================~ [ rust-lang, ijackson@ ] Despite these difficulties, and of course a fair few minor irritations, I have found programming in Rust to be both fun and very productive. I tend to mix languages a lot. I think my record is 6 languages in the same personal project. Some of my recent forays into Rust have involved switching between Rust and C++ and Perl. This kind of thing throws differences between languages into sharp relief. Rust is now my language of choice for many nontrivial programs. Certainly I would try to avoid starting a new program in C or C++. Rust's compile-time type checking and its performance can make it an attractive alternative to scripting languages like Python, Perl and Tcl. Its macro system, and consistent high performance, can make it an attractive alternative to Haskell or Ocaml. I'll still start new programs in many of those other languages, and often I choose a language for its libraries. But for me Rust will now often be high on my list, if I can get away with it. If you want to know more, there's a wealth of stuff online. The place to start is probably https://docs.rust-lang.org/. And of course I'd be happy to help.