Preparing an internal talk for C and Java devs

Why can’t you talk about ownership on itself. We need no multi threading to make this relevant, specially for C, C++ developers.

Well that was my plan initially, but the coworker who asked my the talk (lead dev for the team doing C and C++) specifically asked me to talk about concurrency.

1 Like

Ah there are some good links in there, thanks!

To argue against Python, I've found Rust to significantly increase readability due to the explicitness. There's never a question as to whether a corner case was handled or silently ignored, what input argument types are supported in what positions, what the output argument is, or whether the script will simply break at any point (many Python applications packaged on Linux distributions are broken in some way or another, and this can only be detected by actually running the script and testing each of its features -- needs many integration tests).

I do have an example of a rewrite of a Python application that turned out to be more readable in Rust:

2 Likes

So I've finally settled on some slides. I'll focus on memory safety and concurrency but I do want to say a word about tooling so there's one slide for that. Note that some slides contain invalid rust code (using Vec instead of Vec<T> for instance, or let x instead of let mut x) because I wanted to remove as much noise as possible from the examples where I introduce ownership and borrowing.

Here are the slides, if anyone is interested or has feedback https://github.com/little-dude/rust-talk

Thank you everybody :slight_smile:

1 Like

I think I know what kind of developers you deal with :wink:

Rust has an amazing multi threaded ecosystem. In the case you are cpu bound and you use parallelism to speed processes you should talk about rayon. It follows Intel's cilck philosophy and is just amazing.

In case you are IO bound and you use multi threading to hide latencies and improve response time, have a look to futures and tokio. This is absolute alien for a C developer as there is nothing close to a future at this level. They need to understand that you are getting a very high level abstraction and paying close to nothing for having it.

I hope you enjoy the research you are doing, don't forget to share the outcome!
Cheers

1 Like

Yeah, I'm using Tokio in a bunch of projects and found it pretty amazing although quite hard to approach!

I did learn a lot while preparing this talk!

Sure thing! Thank you!

Alright, I did the talk, and it went pretty well! Thank you everyone! :slight_smile:

9 Likes

Cool! Glad that it went well!

For travelers from the future, who will likely be preparing their own talks, can you share a bit about the feedback you got? What kind of questions? Which themes resonated well? (Constructive) criticism the language received? Those kinds of things :grin:

I think people liked it.

I had a series of questions about what is happening in terms of heap and stack when we move or borrow a value. For example, if I move a vector, is the compiler smart enough not to copy the vector's data? What about big structs, are there memcopied? To be honest, I was not able to answer these questions, which is a shame.

Other questions:

  • how fast is Rust?
  • who uses Rust?
  • what big projects have been written in Rust?

Here's a list: Production - Rust Programming Language

There also used to be an even bigger list here: https://github.com/jonathandturner/rust-big-name-big-list but it has vanished now for some reason :frowning:

As fast as you can make it; in most cases, it compiles down to the same assembly as a C compiler.
Rust also participates in the benchmarks game, where it is consistently in the same league as GCC C

It uses LLVM as its backend compiler, so it has all the optimisation that people are used to. There are some cases where LLVM optimises better than GCC, and some cases where GCC is better than LLVM;
Those cases where Rust is significantly slower are usually due to some mega-optimised C library being available (think handrolled SIMD assembly black magic)
The nice thing about rust is, that in such cases, nobody stops you wrong writing FFI bindings to the C library, and using that. (keyword "bindgen").
Also, the general SIMD ecosystem is still maturing, but has some very concrete proposals in working condition (keywords: "stable SIMD").

This is exactly what the Copy Trait signifies: "Types whose values can be duplicated simply by copying bits."
And a little bit further down: "Under the hood, both a copy and a move can result in bits being copied in memory, although this is sometimes optimized away.".

The poster child is Servo, and now, Firefox Quantum. Anyone who uses the latest firefox, runs rust code in production :slight_smile:

Dropbox has "bet the farm" on Rust; Their storage backend, "magic pocket", was completely rewritten in Rust when they migrated from Amazon-cloud to their own datacenters. (after first trying Go, but that used to much memory). Later, they rewrote their decompression library into rust, to guarantee better memory safety, speed and hacker-resistance.

Also, VLC has rewritten some segments into Rust, namely the parsers for a few media containers. Reason is that the container parsing parses untrusted binary input, often from the internet, so it is very important to application security.

Another one, Tilde, who writes the SkyLight Ruby performance monitoring agent; they recently wrote a whitepaper, on how rust is their competitive advantage.

2 Likes

It is.

Please correct me if I'm wrong, but this non-copying of the vector data does not rely on the the compiler knowing the Vec type, right?

Imagine you have a function that takes ownership of the vector:

fn process_vec(v: Vec<i32>) { ... }

When you call the function, the vector is moved to the function:

let data = vec![1, 2, 3];
process_vec(data);

My understanding is that "moving" here really means "take over ownership" and that it only relates to who is responsible for eventually freeing the memory (calling Drop). Here the compiler will not insert a call to Drop in the calling code but it will insert one at the end of process_vec since that function owns the vector passed to it.

As for the mechanics of the function call itself, the Vec itself is copied onto the new stack frame crated when process_vec is called, but since the vector is just a simple struct consisting of three words, then this is completely acceptable.

The Nomicon has a chapter on the Vec type in which the layout is described as being equivalent to

pub struct Vec<T> {
    ptr: *mut T,
    cap: usize,
    len: usize,
}

The real implementation is more complicated, but the size of a Vec is still just three words of memory. Because this little struct does not actually contain the data elements of the vector, copying it means that you basically just copy a pointer onto the stack — the actual vector data resides somewhere in the heap as indicated by the pointer.

And even there, the compiler is often smart enough to directly write the values into their final location the first time, so even the copy is often saved.

1 Like