Hacker News new | ask | show | jobs
by mseepgood 1652 days ago
> Is it a better bet to move on and go full Rust

If you don't like generics you shouldn't use Rust. You can't escape them in Rust. The designers repeated the mistake of C++ and made it a language feature kitchen sink. It's an unholy mess. You'll find yourself constantly fighting the borrow checker. Type signatures are littered with lifetime annotations. The type system is Turing complete because they didn't analyze it before implementing it. Go's generics were formally validated [1]. Rust's compile time is slow, and the 'async' story is sad. Async functions are colored and infect everything.

[1] https://arxiv.org/pdf/2005.11710.pdf

4 comments

>You'll find yourself constantly fighting the borrow checker.

In the beginning, yes, this is true. But most people learn within a month or two which design patterns lead to problems with the borrow checker and which work smoothly, and often this knowledge translates to good design in languages like C and C++ as well.

If you're fighting the borrow checker in Rust, you'd probably have been fighting segfaults and use-after-free in C / C++. I'd rather spend 30 minutes fighting the borrow checker than spend 4 hours digging around in Valgrind.

> Type signatures are littered with lifetime annotations.

You cannot avoid the concept of lifetimes, without a garbage collector. If you don't want garbage collection, you have to deal with them.

Having explicit lifetime annotations in the code is _vastly_ better than trying to track the lifetimes in your head from scratch every time.

> If you're fighting the borrow checker in Rust, you'd probably have been fighting segfaults and use-after-free in C / C++.

That is in my ( admittedly limited) experience just not true. There's plenty of things that are perfectly safe that the borrow checker just doesn't understand.

The borrow checker can prove that a subset of things is safe. But the borrow checker being unable to prove something doesn't mean it's not safe.

This, one thousands times.

The borrow checker forces you to write in the very narrow subset of code paradigms it can understand. When it fails to compile, it doesn't mean it's wrong: it means that it can't prove that it's correct, which is a completely different statement.

Ah yeah, I am not sure everybody know me what “colored” means but I remember when comparing a C# solution using async with a Go solution using channels and Go routines I finally understood why people keep raving about concurrency in Go. It composed very nicely while any language following the popular async/await approach turns into a total mess. I guess async/await looked good years ago because we compared it with managing POSIX threads manually. That sucked.
It's worth noting that Rust's Async functionality starts with very different priorities from Go's goroutines. Rust, as a systems language, made lack of overhead (allocations, etc) the highest priority. It's part of Rust's overall zero-cost abstractions ethos. Goroutines are just never going to be zero cost. Go chose to prioritize the interface to the programmer, which is much more a part of Go's ethos around being simple.

There's nothing wrong with either approach, they just have different trade-offs because their goals are different. Rust's approach will sometimes push complexity onto the programmer to handle. But it can be made to perform better and more predictably than the Go equivalent. This might not matter if you're not pushing the performance envelope, but if you are, Rust makes that possible in a way that Go simply doesn't. You'd never want to write code using goroutines for an embedded device with limited CPU/memory, but Rust's async is already proving useful for these sorts of projects.

However if you can tolerate the performance overhead that Go imposes, giving the programmer a simpler mental model can easily be worthwhile. Technology is all about trade-offs and you have to choose the right tool for the job.

Async/await was intentionally chosen by a lot of languages well after Go had become popular. Rust once had Go-style concurrency and abandoned it in favor of its current model.
Paraphrasing Stroustrup, there are only two types of languages: those (that end up) with a Turing complete type system and those that nobody use.
A type system being turing complete really isn't a problem. You bound the recursion depth in practice, and the chance of a real world programming hitting that limit is minuscule. Lot's of other languages have turing complete type systems, subtyping and typeclasses lead towards it.
Sure, if you regularly want to increase your #![recursion_limit] or #![type_length_limit] for the next generation of type bloat.
Hmmm I guess it is all in the eye of the beholder. If you are the kind of person who thinks C++ went wrong with its template system, then you might find issue with any language emulating C++ failures.

If you think C++ is a beautiful well deigned language, then I am sure you will not have issues with Rust either.

Java & Haskell both have turing complete type systems as well. I'm not a particular fan of C++'s type system, doesn't mean have metaprogramming features is bad.
C++'s templates go too far — way further than Rust's generics go. Rust's type system may technically be Turning-complete, but nobody's actually doing serious* metaprogramming in it, unlike in C++, where template metaprogramming is a whole discipline unto itself.

* not just toy examples

Right, because Rust provides a real macro system for metaprogramming.