New blog post: Comparing Rust and JavaScript Ergonomics with a Simple Linked List

Thanks to everyone who helped as I was struggling with the Rust code that went into this post: @alexbuzzbee @naavis @Tarithaverin @malinoskj2 @kornel @arjenpdevries @penguin42 @djmoch @Eden

CC @schlink might be up your alley

@codesections interesting read! One question i don't see answered - how long did the two implementations take you to *write*? Would you expect it to vary for a more-experienced rust programmer?



If you're asking how long each version took *me*, then the honest answer is ~20 minutes for the JS version and ~3 days for the Rust version.

…But I'm still in the *very* early stages of learning Rust and I program JS most of the day. And I'm counting a long time that I spent telling myself "surely there's a way to do this without using `unsafe`". I'd bet it'd be about even for someone equally comfortable with both languages.


(Maybe the Rust version would be a *bit* slower to write—hard to tell, since I'm not at that level yet. But, even if it is, I bet the testing/type checking guarantees would make refactoring/using it in the future faster enough to make up for it.)

@codesections @lupine speaking as another newbie, I don't feel like it takes a long time to write Rust. I'm working on a small project of my own and I spend a lot of time looking things up but it's progressing well. Most of my previous experience is with Python, so it's very different working with Rust's type system.

I have to say though that I have less problems with the borrow checker than the Internet led me to believe.

@codesections the website is down for me, but I really hope you didn't just try writing your own linked list implementation in Rust and use that as a way to compare language ergonomics, because linked lists (& trees) is the single worst thing to write in Rust, especially for someone who's new to Rust.

@codesections Rust is just not suited for that (it's possible but not at all nice to write linked lists) — and there are thing JavaScript is not suited for that are easy in Rust (off the top of my head, multi-threading).

Have you read the "learning Rust entirely with too many linked lists" book?

@codesections ah, it loaded, and I see that you did read the book. Good 😀

@codesections the reason for the stack overflow you've been getting is that drop() — which you didn't write explicitly — ends up being recursive. The way you fix that is you write out a manual loop-based drop implementation for your LinkedList. This is covered nicely in the book:

@codesections also a suggestion: every time you mention benchmarking Rust (or C, or any other compiled language) code, do explicitly mention that you did not forget to turn optimizations on (--release for Rust, -O3 for C and so on), because that's a common errors among beginners that benchmark Rust


Thanks for pointing that out! I really *should* have caught the recursive drop issue, since it was mentioned in Learning Rust with Too Many Linked Lists, but I totally missed it. I've updated the post with that fix and the revised benchmarks Rust: ~440 ms; JavaScript: ~800 ms (for 10M nodes each).

Interesting to see that JS narrows the gap a bit with larger lists. Maybe the V8 JIT is able to fully compile a bit more (or just amortize compilation time over more runs, I guess).

@codesections TBH this is not an ideal benchmark for Rust vs JS performance either, because even in Rust, you have to separately heap-allocate every node. The 2x-3x improvement you get is still very nice, but on more realistic tasks/benchmarks Rust can be *way* faster than scripting languages because of how it can avoid allocations.

See, for example,

@codesections oh btw! Could you also benchmark Rust's usual Vec the same way? It's not a linked list, but it still might be fast for the same operations.


Vec doesn't seem particularly comparable to me—it's an entirely different data structure that should have different performance characteristics. In particular, I'd expect that the compiler can optimize pushing into a Vec in a loop into creating a larger Vec. But, since you asked, the same code take ~70ms with a Vec.

(A better comparison is probably std::Collections::LinkedList, which takes ~550ms but gives you a doubly-linked list instead of a singly-linked one)

@codesections I've made a few improvements to your code:

"Maybe JavaScript ekes out a technical victory by having equally expressive code in a couple fewer lines, but it's not by much." is no longer the case 😉 and note that contains() does not need a &mut, and that unwrap() is gone...


Thanks—I like those improvements! My only counter-suggestion would be that you can consolidate your lines 39-41 to a single line by moving the `;` outside the block (so that you get `unsafe { (*old_tail).next = Some(new_tail) };`).

That works because rustfmt allows single-line unsafe block so long as the entire block is a single statement—which ours can be.

@codesections you can do it, but I (slightly) prefer it this way — it's just a personal preference though


> linked lists (& trees) is the single worst thing to write in Rust

Interesting to hear you say that—I came away from writing a linked list in Rust thinking it was a very nice language to write one in. So if it really *is* a bad use case for Rust, then I'm excited to see what writing Rust feels like with a *good* use case. 😀

@codesections ha, yes, I'm (pleasantly) surprised that you liked the experience — you had to use 😮 unsafe 😮 and double-check invariants manually and the compiler didn't have your back. And yes, then you'd like "normal" Rust experience even more 😀

Sign in to participate in the conversation

Fosstodon is a Mastodon instance that is open to anyone who is interested in technology; particularly free & open source software.