Follow

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

codesections.com/blog/javascri

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?

@lupine

Thanks!

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.

@lupine

(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: cglab.ca/~abeinges/blah/too-ma

@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

@bugaevc

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, web.archive.org/web/2018122808

@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.

@bugaevc

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: play.rust-lang.org/?version=st

"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...

@bugaevc

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

@bugaevc

> 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

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