Live data from Hacker News

Tell HN: Rust Is Complex

news.ycombinator.com

1–10 of 102 posts

Tell HN: Rust Is Complex

#1
I'll preface this by saying I like Rust, and I've found myself coding more in Rust the last two years than anything else. But Rustaceans kind of like to laugh at Go, because it's not as expressive or elegant a language by comparison. That's mostly true, think of how nicely Option types, enums, and iterators work in Rust compared to Go. However, Go is simple and deeply pragmatic. There is an underrated value in that. Some parts of Rust are starting to remind me of the horror I ran from with C++. Look at this:

Update: It is possible to abuse existing CoerceUnsized implementations on stable. See #85099 (although I created that issue before reading any of this issue and its IRLO thread, so don’t expect any syntactic similarity to the unsoundness examples of this issue).

The type Pin implements Deref but it doesn’t implement DerefMut. The types Pin and & are #[fundamental] so that an impl DerefMut for Pin> is possible. You can use LocalType == SomeLocalStruct or LocalType == dyn LocalTrait and you can coerce Pin> into Pin>. (Indeed, two layers of Pin!!) This allows creating a pair of “smart pointers that implement CoerceUnsized but have strange behavior” on stable (Pin and Pin become the smart pointers with “strange behavior” and they already implement CoerceUnsized).

More concretely: Since Pin: Deref, a “strange behavior” DerefMut implementation of Pin can be used to dereference an underlying Pin into, effectively, a target type (wrapped in the trait object) that’s different from SomeLocalStruct. The struct SomeLocalStruct might always be Unpin while the different type behind the &mut dyn LocalTrait returned by DerefMut can be !Unpin. Having SomeLocalStruct: Unpin allows for easy creation of the Pin> which coerces into Pin> even though Pin::Target: !Unpin (and even the actual Target type inside of the trait object being returned by the DerefMut can be !Unpin).

Methods on LocalTrait can be used both to make the DerefMut implementation possible and to convert the Pin (from a Pin::as_mut call on &mut Pin>) back into a pinned mutable referene to the concrete “type behind the &mut dyn LocalTrait returned by DerefMut”.

From: https://github.com/rust-lang/rust/issues/68015#issuecomment-835786438

Where's that elegance now? I still maintain that I'd rather use Go when the problem domain allows for it (e.g. can use a garbage collector, don't need fast interoperability with C, don't need maximum performance.)

Re: Tell HN: Rust Is Complex

#2
Question: How much of Rust’s type system complexity is due to async? It seems Pin is mostly related to async and the official examples for GAT are for async.

Given the complexities of async and how other alternatives such a Java Loom’s virtual threads, in about 10 years I am not sure that we won’t see Rust going all in on async as a mistake.

Re: Tell HN: Rust Is Complex

#3
The problem with go is not it's simplicity, it's it's inconsistency. As a small-minded programmer my hobgoblin is tripping over having to remember that X works this way but not Y, which looks vaguely like x.

It feels like the designers of go tripped over themselves to make things simple for some definition of simple and then ran into some feature they wanted and then just bolted the feature on without thinking about whether simplicity/consistency tradeoffs were a thing.

Re: Tell HN: Rust Is Complex

#4

The problem with go is not it's simplicity, it's it's inconsistency . As a small-minded programmer my hobgoblin is tripping over having to remember that X works this way but not Y, which looks vaguely like x. It feels like the designers of go tripped over themselves to make things simple for some definition of simple and then ran into some feature they wanted and then just bolted the feature on without thinking about…

Do you have more examples? I’ll admit that Go is full of weird quirks, but nothing feels bolted on. Generics were agonized over for so long because they had to fit it within the existing language

Re: Tell HN: Rust Is Complex

#5

Question: How much of Rust’s type system complexity is due to async? It seems Pin is mostly related to async and the official examples for GAT are for async. Given the complexities of async and how other alternatives such a Java Loom’s virtual threads, in about 10 years I am not sure that we won’t see Rust going all in on async as a mistake.

Since we’re comparing Go and Rust, I think Go really nailed it with goroutines and channels. It’s much easier to get your head around than async.

Re: Tell HN: Rust Is Complex

#6

Question: How much of Rust’s type system complexity is due to async? It seems Pin is mostly related to async and the official examples for GAT are for async. Given the complexities of async and how other alternatives such a Java Loom’s virtual threads, in about 10 years I am not sure that we won’t see Rust going all in on async as a mistake.

> I am not sure that we won’t see Rust going all in on async as a mistake.

I'm about a month into Rust, but Rust didn't go all in to async; it only went halfway in, which is why some functions are async and others aren't.

Erlang is all in on async/green threads/tasks. From what I gather, so is Loom. There's no special way to write async code, you just write regular code and it works; for Loom you just spawn your threads a little differently. For Rust, you have to be somewhat careful about what you do or don't do in a task, and you have to write .await everywhere.

Re: Tell HN: Rust Is Complex

#7

The problem with go is not it's simplicity, it's it's inconsistency . As a small-minded programmer my hobgoblin is tripping over having to remember that X works this way but not Y, which looks vaguely like x. It feels like the designers of go tripped over themselves to make things simple for some definition of simple and then ran into some feature they wanted and then just bolted the feature on without thinking about…

Do you have more examples? I’ll admit that Go is full of weird quirks, but nothing feels bolted on. Generics were agonized over for so long because they had to fit it within the existing language

Here’s one: The fact that contexts are used extensively in the standard library but that there’s still no way to cancel a write to an io.Writer. The only way to “cancel” a write on a TCP socket is to set the socket’s “deadline” to some time in the past from another goroutine.

Re: Tell HN: Rust Is Complex

#8
Complexity is relative.

Go is a language for high-level application development. It has a runtime, garbage collection, and a sophisticated M:N threading scheduler that are all designed to help the programmer express themselves in terms of the problem domain. It competes with Java, C#, and (in some cases) Python or Ruby.

Rust is a language for low-level systems programming. It needs far more complexity than Go because the programmer might need to make extremely specific guarantees about performance or memory layout. It competes with C++ and C.

Is Rust a more complex language than C++? It's not obvious to me that this is true when you add in the third-party tooling (linters, static analysis) required to provide Rust's correctness properties in a C++ codebase.

Is Rust a more complex language than Go or Java? Sure. Obviously. Anyone who says it's not is lying or foolish. But that's not the comparison being made.

Re: Tell HN: Rust Is Complex

#9
Cherry picking an instance of complexity is not a particularly fair way to judge a language. It would be like explaining how to encode the lambda calculus in C++ templates and then claiming C++ is too complex.

As far as I have read, Pin has never been a satisfactory thing but a necessary one. Even recently Rust has been trying to decide how to really handle unsafe mutable pointers in a way that COULD be safe. I'm surprised you didn't bother to use that example, as it is much more convincing in my opinion.

But that gets to the point, not all of Rust is simple (Indeed, most novices will exclaim it is way too complex for merely having a borrow checker) or elegant, but that does not prevent the average use case from being so (the exact situations that you claim to want to use Go in, why would you be using two nested Pins in any normal situation?). Moreover, Rust is actively evolving, and we can hope that these rough edges do get better with time.

Re: Tell HN: Rust Is Complex

#10

Question: How much of Rust’s type system complexity is due to async? It seems Pin is mostly related to async and the official examples for GAT are for async. Given the complexities of async and how other alternatives such a Java Loom’s virtual threads, in about 10 years I am not sure that we won’t see Rust going all in on async as a mistake.

Since we’re comparing Go and Rust, I think Go really nailed it with goroutines and channels. It’s much easier to get your head around than async.

[deleted]
Post reply on HN