Felix Klock describes the core concepts of the Rust language (ownership, borrowing, and lifetimes), as well as the tools beyond the compiler for open source software component distribution (cargo, crates.io).
Felix Klock is a research engineer at Mozilla, where he works on the Rust compiler, runtime libraries, and language design. He previously worked on the ActionScript Virtual Machine for the Adobe Flash runtime. Klock is one of the developers of the Larceny Scheme language runtime.
Software is Changing the World. QCon empowers software development by facilitating the spread of knowledge and innovation in the developer community. A practitioner-driven conference, QCon is designed for technical team leads, architects, engineering directors, and project managers who influence innovation in their teams.
- The most important reason to use Rust is safety and parallelism.
- Ownership is an important concept and the reason is that it removes a whole class of issues.
- Why do we need multiple reference types? Because it's fundamentally true.
- Rust provides the primitives for third parties to provide alternative parallelism APIs.
- Cargo works by salting the symbols that are embedded in the generated code to include the version number information.
Why should you be using Rust?
- 0:30 - The simple answer that it's we're targeting the same market is C and C++. We want to have control of your generated code, with fast code and a really low memory footprint with no garbage collection at all, giving explicit memory management, but it's still easy to use.
- 0:52 - [Rust] provides the bare metal abstraction that the hardware gives you, and gives you a C foreign function interface and access to inline assembly.
- 1:17 - We don't impose the cost that's often associated with constructs like this. Closures often have a cost. Having arbitrary collections can impose a cost. There's various ways that we try to get down to zero cost for all these abstractions.
- 1:34 - The most important reason to use Rust is safety and parallelism. Safety means no more segmentation faults: if you stick within the safe part of Rust and don't use the unsafe keyword then you should never see any undefined behavior.
- 2:17 - As for parallelism, Rust is multi-paradigm. If you want to use message parsing, we provide abstractions for that. If you want to use shared state, Rust provides support for that atomic reference counts.
Why Mozilla is Working on Rust
- 3:09 - Netscape is not dead, it just converted into something else. At Mozilla we've got a lot of really smart people and they've got amazing ideas on ways to make the Internet better, and to make the browser better. But it's hard to prototype them on a C++ codebase that we're dealing with.
- 3:29 - This was the impetus on us to invest and sponsor Rust as a project, in order to make the server web browser on which we've done various experiments on next gen technologies for browser, for example web render is using the GPU to do browser rendering and getting amazing frames per second.
- 3:51 - [Mozilla has] come to realise is that Rust is also deployable for next gen infrastructure for the Internet, services, and Internet of Things and that tes into Mozilla's overall mission as a non-profit is to ensure the Internet is a global resource.
- 4:10 - There's a phrase: "open and accessible to all" which ties into what we're discovering about Rust, as a community, and as a phenomenon.
Rust Right Now
- 4:22 - Rust had its 1.0 release in May last year and follow a rolling release cycle, where every six weeks we turn the beta into the stable version and turn the nightly into the beta.
- 4:54 - We invite our community, and invite anyone to come join our community, and help advise us on how to make this help push things forward.
- 5:04 - [In the Rust community] we have a thousand people in our general Rust users area, 250 people in the internals area where people are working on changing the language on the runtime and the libraries. Yhere's also the history for the gihub repository, with 1300 unique commits.
Rust and Ownership
- 9:57 - Thinking of bitcoin. If I withdraw some cash from a bank and I have an actual object to represent that money, and I buy a car with the money transferring it to a dealership. If in Rust you are tempted to write a line that said buy another car with the same money in the same program, this gets reject by the compiler the second car. You can't reuse a resource like that. That's ownership.
- 10:24 - When we have my new car I might drive it home and then I might park in the garage. I'm transferring my new car into the garage. That's at least what the code is written to do there, which means that if I try to do something else with my new car that doesn't work either. We have to instead move it out of the garage. We take it out, unpark from the garage, and now we can extract that variable and drive it to work. This is like a linear type system.
- 12:07 - This is a fundamental confusion, confusing the resource of a home with the name of a home or the reference to the home. You have to distinguish these things, as in C or C++ and even Java.
- 12:37 - Ownership is an important concept and the reason is that it removes a whole class of issues and bugs that arise. It's the fundamental thing that we build up these other properties.
Sharing in Rust
- 13:54 - We have a collection of types in Rust, things like vectors and dynamically growing arrays of instances of T, or string buffer-- like in Java -- where it's a string that might be dynamically expanded to fit whatever data you're putting into it.
- 15:00 - [in Rust] We have structures that's the way we were doing records in Rust. When you can create instances of structures you either use type annotations on your bindings or you can leave them out, we use type inference.Rust has two kinds of core references in language. We have so-called references. And then we have the immutable references.
- 15:27 - Why do we need multiple reference types? Because it's fundamentally true. It's distinguishing two important things: exclusive access versus shared access.
- 16:48 - We're trying to prevent having mixing exclusive access with shared access, fundamentally this doesn't make any sense. We're doing it at compile time and the way we're doing it is with this family of types. We have types T, they're owned. We have the exclusive access references, &mutT, and the shared access &T.
Slices: borrowing parts of an array
- 23:03 - The crucial idea is that when you have a pristine vector you can create references to sub ranges of it. The way this works in Rust is that it ensures soundness by having the reference carry both the pointer to the start of the reference the start of the slice and its length.
- 24:13 - If you want to break down your array and do recursive quicksort on it this is the crucial piece of the puzzle, Rust has support for this in the language slash runtime.
- 24:31 - [Rust has] reference counting, particularly the R C type. Now we have two arcs that are solid, this means that ownership is shared between the two. These two are ones are two and since they're sharing they only have read access, they can't have write access because it's shared. Neither can statically assume exclusive (mut) access and they can't provide &mut borrows guaranteed.
- 25:20 - [Refcell] dynamically tracks in the allocated object what kind of borrowers are currently active. If it's only reader borrows that means that you can't have exclusive access.
- 26:50 - We can have reference counted pointers to ref cells and so all the same patterns work there. Which means that once you have an RC of ref cell you're sort of back in the world of arbitrary sharing and arbitrary attempts to mutate. You can re-express all of your old algorithms the same with the same patterns as before.
- 27:51 - There's lots of other kinds of shared ownership that are provided as library types. RC and arc aren't actually built in the language, and neither are these.
Sharing Work: Parallelism/Concurrency
- 32:02 - The standard thread abstraction that the standard library provides doesn't allow sharing stack level data. If I have an array of integers and I try to spawn threads that print out particular elements, there's no guarantees known about the relationship between those spawned threads and the lifetime of the stack frame and so the compiler rejects this code.
- 32:50 - Cross-beam offers scope threading abstraction, and cross-beam enforces that those threads are guaranteed to be joined by the end of the caller stack frame which means that now you can share references from a local stack frame into the threads.
- 33:37 - Niko Matsakis has a library called Rayon that might be parallelism.
- 34:06 - We're getting the same property that we're able to spawn off a thread that's accessing locally owned data tby the stack frame, but Rayons can ensure that all the threads that are spawned off if any join before it finishes.
- 34:21 - Rayons Rule is that it decides whether to make use of parallel threads based on dynamic properties, like how many idle cores are available.
- 35:46 - Rust provides the primitives for third parties to provide alternative parallelism APIs.
Soundness and 3rd Party Concurrency
- 36:43 - A type is Send, which means it can be transferred from one thread to another, it can be moved, or, if the data is copyable, it can be copied. It's only implements Send if it's safe to do that transference.
- 36:55 - A type is Sync if two threads can safely both share a reference to it.
Sharing Code: Cargo
- 39:25 - Cargo is our answer to sharing code.
- 42:17 - Cargo has strict adherence to semantic versioning... when you have a crate and you change its behavior in some way that's backwards incompatible, that's when you increment the major version number. If you do something that's backwards compatible but adds new API then you update the minor version number, and then if all you're doing is just making a small bug fix and you're not adding a new API then you update the patch number.
- 42:47 - In Rust, a breaking change includes things like a changed representation of the data structure. This gets embedded when you create an instance of a record from another record. It ends up being embedded: The layout of that record ends up being embedded.
- 43:16 - The way cargo works is it salts the symbols that are embedded in the generated code to include the version number information in a way so that two independent libraries If you've got a diamond dependency or two libraries use the same thing, if they're distinct major versions there won't be any conflicts. You won't be allowed to even pass data in between the two. The compiler wins sure none of that's allowed.
- 46:52 - Rust is meant to be deployed and embedded into pre-existing systems and pre-existing applications.
See more presentations with show notes
Link to Slides (easier to see drawings)
Here is a link to the same slides (they are just a reveal.js web page):