Live data from Hacker News

Fearless concurrency with Rust

blog.rust-lang.org

71–80 of 186 posts

Re: Fearless concurrency with Rust

#71
post #63
post #51

Earlier quoted context omitted.

> First, please don't compare Go and Rust. They are completely different languages with completely different target use cases. Then why does everyone insist on comparing Rust and C++? Objectively Rust is much closer to Go than to C++. That's just dumb. Rust and Go were, in fact, contemporarily designed to similar constraints and with similar goals. That they made significantly different design choices is an interesti…

What objective criteria are you using by which Rust is much closer to Go? Go being garbage collected and Rust/C++ not being seems like a much larger gap than anything between Rust and C++.

A managed heap; emphasis on the use of functional constructs; rejection of lots of previously-thought-to-be-essential OOP features; built-in concurrency based on a rejection of the traditional primitives; general de-emphasis of metaprogramming constructs like templates and macros; broad design emphasis on preventing common programming mistakes.

You're saying, I think, that the Go implementation and runtime looks more similar to Java and that C and Rust are clearly in the same family (in the sense of being only losely coupled to a runtime environment). That's the way a language implementer might look at the question, I guess. It's certainly not the only one.

Re: Fearless concurrency with Rust

#72
post #12

Rust's ownership model, and the borrow checker that enforces it, are a major breakthrough in language design. It's so simple, yet it solves so many problems. This is what Go should have done. Then Go's "share by communicating, not by sharing" would be real, not PR. Go code often passes references over channels, which results in shared memory between two threads with no locking. In Rust, when you do that, you pass own…

First, please don't compare Go and Rust. They are completely different languages with completely different target use cases. Go has very different design goals than Rust, so very little that Rust does would actually be possible in Go, and vice versa. > Go code often passes references over channels, which results in shared memory between two threads with no locking Having a complex type system would cut into compile t…

> Go has very different design goals than Rust, so very little that Rust does would actually be possible in Go, and vice versa.

Oh give this lame argument a rest already. They're both supposedly general-purpose programming languages. Rust shoots for a little lower level than Go, but they can certainly be compared, and the comparisons are valid.

> Having a complex type system would cut into compile times (an explicit primary design goal, to the point where the Go compiler must be written to read each file exactly once[0], no more).

I'm not sure how this is a defense of Go: yes, that's a primary design goal; it's also a terrible design goal. Trading an adequate type system for a short-term gain in compile time is an amateurish mistake.

Compilation time can be mitigated on even the largest projects by only rebuilding changed modules. If you're getting to the point that this isn't working, then maybe it's time to start working on reducing the size of your code.

I've worked on projects that were 500,000 lines of code and compile time was sometimes an issue, but not nearly as large an issue as bugs which would have been caught by an adequate type system (large C#/Java codebases pre-generics).

> As for the code you're referring to, Go is not designed to be a language which prevents you from shooting yourself in the foot with provable code. It's designed to be a language which makes it reasonably easy to be sure you haven't, as long as you follow the general idioms and best practices. What you're describing here is definitely not one - I can think of a few instances in which pointers might be reasonably passed over a channel, but they're few and far between.

We've tried this approach before, many, many times, and the result has been high-profile bugs.

The fact is, these "idioms and best practices" will not be followed perfectly on projects of any reasonable size if they are not enforced by code. Why would you not enforce them with code? And if you're designing a language, why not design the language in such a way as to allow code enforcing best practices? Like, a type system?

> Also, I would think it's pretty obvious that once you've sent something over a channel you shouldn't try and write to it anymore[1].

Yes, just like it's pretty obvious that when you free memory you shouldn't write to it any more. We've never had any issues with that, now have we?

> It's definitely possible to extend `go vet` to handle this, and it may even be possible to write this in as a compiler error in a future version of Go.

I didn't know about `go vet` and I wish I didn't.

So, to avoid a second pass over the code during compilation, you add a second pass over the code during `go vet` that has less capabilities. Good thinking. I'll add that onto the list of other stupid ideas that have been tried decades ago and didn't work but are finding new life in Go.

> Incidentally, one of the reasons that it's so easy to do reliable static analysis on Go code (compared to other languages) is that the grammar is incredibly simple - it's almost entirely context-free, which is very rare among non-Lisps. Having a more complex type system usually requires at least some additional syntax to along with this, which means you'd have to start sacrificing this design goal as well in order to create a more elaborate type system.

Yes, adding syntax for static type checking will definitely make static analysis like type checking harder. Are you fucking kidding me?

Re: Fearless concurrency with Rust

#73
post #64
post #12

Rust's ownership model, and the borrow checker that enforces it, are a major breakthrough in language design. It's so simple, yet it solves so many problems. This is what Go should have done. Then Go's "share by communicating, not by sharing" would be real, not PR. Go code often passes references over channels, which results in shared memory between two threads with no locking. In Rust, when you do that, you pass own…

"Should have done" is a silly phrase to use here -- Rust is attempting to advance the state of the art, Go was attempting to use only very-well-understood ideas to make a very-well-understood, simple language. Perhaps some day we will see a successor language that follows the philosophy of Go using the new stuff we understand thanks to Rust.

> Go was attempting to use only very-well-understood ideas to make a very-well-understood, simple language.

Well-understood like code generators (go generate)? Were generics not well-understood enough, so they decided to go with something that's well-understood to be a poor solution to the problem?

Re: Fearless concurrency with Rust

#74
post #3

This post observes that > the same tools that make Rust safe also help you tackle concurrency head-on. The tools that make Rust safe, in this instance, being its ownership type system. Why on earth should that be the case? Why would ownership types make concurrency easier? The post gives plenty of in-depth answers to this question, but to my mind it misses the bigger picture. The big-picture reason why ownership type…

I wonder how much the concept of owner/borrows could work in a more scripting/dynamic language. Exist some sample on that direction?

I'm working in a toy language focused in data (relational), on top of F#.

Re: Fearless concurrency with Rust

#75
post #74
post #3

This post observes that > the same tools that make Rust safe also help you tackle concurrency head-on. The tools that make Rust safe, in this instance, being its ownership type system. Why on earth should that be the case? Why would ownership types make concurrency easier? The post gives plenty of in-depth answers to this question, but to my mind it misses the bigger picture. The big-picture reason why ownership type…

I wonder how much the concept of owner/borrows could work in a more scripting/dynamic language. Exist some sample on that direction? I'm working in a toy language focused in data (relational), on top of F#.

Ownership is doable in a dynamic language, provided you are willing to tolerate aborts: C++'s std::unique_ptr is enforced at runtime. Similarly, you can emulate dynamic borrowing using something like Rust's RefCell, which aborts when you attempt to mutably borrow a value twice (though it can be quite difficult to reason about from a usability perspective).

But if you're building on top of F# you have a static type system at your disposal. Usually, languages that incorporate linear types and first-class garbage collection treat linear types as a separate kind, and allow them to interact with standard types only in limited ways. See, for example, https://github.com/idris-lang/Idris-dev/wiki/Uniqueness-Type.... I believe http://protz.github.io/mezzo/ is also exploring this design space.

Re: Fearless concurrency with Rust

#76
post #71
post #63

Earlier quoted context omitted.

What objective criteria are you using by which Rust is much closer to Go? Go being garbage collected and Rust/C++ not being seems like a much larger gap than anything between Rust and C++.

A managed heap; emphasis on the use of functional constructs; rejection of lots of previously-thought-to-be-essential OOP features; built-in concurrency based on a rejection of the traditional primitives; general de-emphasis of metaprogramming constructs like templates and macros; broad design emphasis on preventing common programming mistakes. You're saying, I think, that the Go implementation and runtime looks more…

> A managed heap

I'm not sure what you mean by this, but when I hear "managed heap" I think of heap memory managed by a garbage collector. This is not a feature of Rust.

> emphasis on the use of functional constructs

Rob Pike wrote (unavoidably, because of the lack of generics) crippled versions of map and reduce for Go and declared that the almightly for loop was superior. I don't think an emphasis on the use of functional constructs follows from this.

> rejection of lots of previously-thought-to-be-essential OOP features

Yep, this is an important similarity between Go and Rust. Rust brings traits, which are (extremely) similar to Haskell's typeclasses, into the mix as well.

> built-in concurrency based on a rejection of the traditional primitives

Sure, but the differences between the languages is crucial here: Go builds safe(ish) concurrency primitives into the language; Rust does not, but provides language features powerful enough to build memory-safe concurrency primitives in the standard library. And, perhaps surprisingly, mutexen and the like are actually encouraged precisely because Rust can make them safer.

> general de-emphasis of metaprogramming constructs like templates and macros

I don't agree that Rust de-emphasizes these. Generic programming is strongly encouraged, and indeed Rust's generics are implemented very similarly to C++ templates. Rust also strongly encourages the use of macros for cleaning up repetitive blocks of code, both inside of and outside of function bodies. And very powerful metaprogramming is on the way (already available in nightlies) in the form of compiler plugins, which allow arbitrary code execution at compile time.

> broad design emphasis on preventing common programming mistakes.

I think this is a feature, or at least an intended one, of every higher-level programming language :-)

Re: Fearless concurrency with Rust

#77
post #12

Rust's ownership model, and the borrow checker that enforces it, are a major breakthrough in language design. It's so simple, yet it solves so many problems. This is what Go should have done. Then Go's "share by communicating, not by sharing" would be real, not PR. Go code often passes references over channels, which results in shared memory between two threads with no locking. In Rust, when you do that, you pass own…

First, please don't compare Go and Rust. They are completely different languages with completely different target use cases. Go has very different design goals than Rust, so very little that Rust does would actually be possible in Go, and vice versa. > Go code often passes references over channels, which results in shared memory between two threads with no locking Having a complex type system would cut into compile t…

> They are completely different languages with completely different target use cases. Go has very different design goals than Rust, so very little that Rust does would actually be possible in Go, and vice versa.

But everyone keep trying to though, that's kind of interesting.

The reason I am guessing is because Go billed itself as a "systems programming langauge". It did, go find the original announcement video (or was it a press release). That is what it said.

Later after replacing C idea didn't quite materialize. They clarified that they mean "systems programming language" to mean something else actually. Now if I said that or some other anonymous user, ok, fine, people get confused, don't know enough etc etc. But I don't believe Rob Pike doesn't know what a systems programming language is.

Anyway, I am not saying one way or another but just illustrating that everyone doing the comparison is not completely insane.

Now they way I see Go is more like a Python++ or Ruby++. All those nice concise languages, + concurrency, + speed, + some type safety, + easy static binary deployment. So I agree with you that Rust and Go should not be compared.

Re: Fearless concurrency with Rust

#78
post #69

Earlier quoted context omitted.

I thought Cyclone only used regions, but I haven't read the paper in a while. (and dates from 2002 IIRC) It certainly had a big influence on Rust, we're fans of the work.

It looks like you're right. I had an undergrad senior thesis in 2006 that futzed around with regions and borrowing, and I know I read some papers on their interactions, but I'm not immediately finding anything chasing references. edit: Of course, I don't mean to diminish the accomplishments of Rust, which had turned these moving parts into a working language. In particular, Rust makes it easy to ignore these issues m…

Absolutely no diminish-ment taken.

To put it in startup terms: other people working on your idea only validates it, it's not a threat. Execution is ultimately what matters. I prefer letting academia blaze the intellectual trail, and then have a number of people implement it after. :)

Re: Fearless concurrency with Rust

#79

Earlier quoted context omitted.

First, please don't compare Go and Rust. They are completely different languages with completely different target use cases. Go has very different design goals than Rust, so very little that Rust does would actually be possible in Go, and vice versa. > Go code often passes references over channels, which results in shared memory between two threads with no locking Having a complex type system would cut into compile t…

> Go has very different design goals than Rust, so very little that Rust does would actually be possible in Go, and vice versa. Oh give this lame argument a rest already. They're both supposedly general-purpose programming languages. Rust shoots for a little lower level than Go, but they can certainly be compared, and the comparisons are valid. > Having a complex type system would cut into compile times (an explicit…

> The fact is, these "idioms and best practices" will not be followed perfectly on projects of any reasonable size if they are not enforced by code. Why would you not enforce them with code?

Compiler shall not be an obstacle to a man. We tried enforcing things before, many times. Things got abandoned.

Re: Fearless concurrency with Rust

#80
post #64

Earlier quoted context omitted.

"Should have done" is a silly phrase to use here -- Rust is attempting to advance the state of the art, Go was attempting to use only very-well-understood ideas to make a very-well-understood, simple language. Perhaps some day we will see a successor language that follows the philosophy of Go using the new stuff we understand thanks to Rust.

> Go was attempting to use only very-well-understood ideas to make a very-well-understood, simple language. Well-understood like code generators (go generate)? Were generics not well-understood enough, so they decided to go with something that's well-understood to be a poor solution to the problem?

Actually, now that you mention it, I think generics are poorly understood in general. C++, Java, and C# have major differences in their approach to generics, but they have similar syntax so most people gloss over the differences. I think doing generics well requires incorporating higher order types, higher kinded types, and other concepts which don't exist at all in the wild west of C++ templates. This clumsy implementation leads to monstrosities like std::allocator_traits>::rebind_traits, which would be completely unnecessary if C++ had HKT. Java and C# make other trade-offs but also lack HKT and suffer for it.
Post reply on HN