top | item 12921006

(no title)

fizzbatter | 9 years ago

So, i was a Go developer for ~4 years, then for the last 4 months or so i've been learning and using Rust. The pure joy of some things with Rust was astounding.

Now, i joined a new job and they're in need of a new language for some backend tasks - the choice was mine. Rust or Go? The backend tasks were heavy API servers - nothing amazing, we don't do groundbreaking work. Python was their existing language, but they weren't too happy with it.

For weeks (literally) i mulled it over, i really really really wanted to use Rust. Rust gives me a peace of mind that Go never came close to. Unfortunately, Rust does not fit the development speed nor developer experience[1] that this shop has.. and i really felt Go would be the best choice for this place.

My point to this post isn't to make a Rust vs Go comparison.. but rather, a wishlist for the hopefully eventual Go 2.0. I have no idea if/when that will ever be made.. but i hope it will, and i hope they adopt some things from Rust.

1. Channels without panics. Channels are awesome, but Go's design of them means that you have to learn special ways to design your usage of channels so that it does not crash your program. This is asinine to me. So much type safety exists in Go, and yet it's so easy for a developer to trip over channels and crash their program.

2. Proper error handling. I love error checking - i love it in Rust especially. It's very explicit, and most importantly, very easy to check the type of things. Recently i was reading an article about Go errors[2] and it made me realize how messy Go errors are. There are many (three in that article) ways to design your error usage, and worst of all your design doesn't matter because you have to adapt to the errors returned by others. There is no sane standard in use that accounts for the common needs of error checking.

3. Package management. It's a common complaint, i know. But Rust & Cargo is so excellently crafted.. Go just got it wrong. Recently i've been using Glide, and while it's a great tool, there is only so much it can do. It's a tool built to try and put some sanity in a world where there is next to no standardization. We need a Go package manager.. hell, just copy Cargo.

4. Enums. My god, Enums. Such a simple feature, but so insanely welcome and useful in Rust.

You'll note that i didn't list Generics. I know that's high on peoples list, but not mine. To each their own.. please don't start a holy war. (this is likely due to me using Go for ~4 years. I'm quite comfortable without Generics)

[1]: By experience, i mean what they desire/expect. They're a Python shop after all, i didn't want to make a choice that would cause them to spend weeks/months feeling unproductive. [2]: http://dave.cheney.net/2016/04/27/dont-just-check-errors-han...

edit: minor text fixes.

discuss

order

jerf|9 years ago

"Channels without panics. Channels are awesome, but Go's design of them means that you have to learn special ways to design your usage of channels so that it does not crash your program. This is asinine to me. So much type safety exists in Go, and yet it's so easy for a developer to trip over channels and crash their program."

I've solved this in my programming by finally coming to grips with the fact that channels are unidirectional, and if you want any bidirectional communication whatsoever, up to and including the client telling the source for whatever reason it has closed, even that one single bit, that must occur over another channel of its own. Clients must never close their incoming channels. This does mean that many things that Go programmers sometimes try to bash into one channel need to be two channels.

But I agree it's a problem.

masklinn|9 years ago

> I've solved this in my programming by finally coming to grips with the fact that channels are unidirectional, and if you want any bidirectional communication whatsoever, up to and including the client telling the source for whatever reason it has closed, even that one single bit, that must occur over another channel of its own. Clients must never close their incoming channels. This does mean that many things that Go programmers sometimes try to bash into one channel need to be two channels.

Erlang got it right (again), by sending messages to processes they can only be unidirectional.

fizzbatter|9 years ago

I don't know how, but frankly i'd love to eliminate all simple panics. Nil pointers and channels seem two big culprits, offhand.

Granted, i left out nil pointer/interface panics because it seems unrealistic given how difficult it was for Rust to get rid of nil pointers. I'm not sure Go 2.0 could do it and still be considered Go.

JulianMorrison|9 years ago

Isn't that a timing race? You're relying on the notification of "I'm done" getting there before the other thread tries to send.

Write-and-panic has the advantage of being atomic. And you can catch a panic.

If you have a stoppable reader like that, maybe have it pull using a channel of channels. That is, consumer sends the producer "here is a one use unbuffered channel, please put an item in it" and then blocks waiting for the response.

Matthias247|9 years ago

I don't think panics on channels are a big problem. Mostly they are a symptom of bad architecture. In a good design the ownership of each end of the channel is exactly defined, similar like you would have to define the ownership for all resources in Rust. As soon as that's the case the owner can (on demand) close the channel and others can read from it and wait for the close/finish.

The only corner case is multi-producer channels, which either need additional synchronization or could be left open and garbage-collected.

brandur|9 years ago

I've taken a very similar path recently myself and started looking into Rust.

> 2. Proper error handling. I love error checking

Hugely agree here. I can get behind Go's overall mentality of returning errors instead of throwing exceptions, but in my mind there are not enough primitives in the language to keep this style of coding safe and sustainable.

Rust's `Result` type, `try!` macro, and pattern matching are an incredibly powerful combination, and make Go's approach look downright medieval in comparison.

Overall though, Go is probably still the more practical choice between the two languages (due to Rust's incredibly high barrier to entry).

olauzon|9 years ago

> Overall though, Go is probably still the more practical choice between the two languages (due to Rust's incredibly high barrier to entry).

Does Rust really have an "incredibly high barrier to entry?"

I've been using Rust for a few months, and just deployed my first high-throughput application a month ago, and my experience has been the opposite. Yes, the first couple of weeks were a bit rough while I was getting used to the ownership system, but since then I have been progressing at a relatively quick pace. The package and dependency management facilities are incredibly good, and I've found high-quality libraries for nearly all my initial needs.

Compilation times could be faster, but the error messages provided by the compiler are so useful that I have come to depend on compilation errors for refactoring. The gains in predictable performance and resource utilization have far outweighed any initial cognitive overhead in the learning process. The community and the resources they provide are fantastic.

Coming from a mixed dynamic language and functional programming background, I could see room for improving certain FP aspects of the language, but am impressed with the pervasive pattern matching and collection handling.

Not a knock on Go, but rather an endorsement of Rust and its future.

pcwalton|9 years ago

> Overall though, Go is probably still the more practical choice between the two languages (due to Rust's incredibly high barrier to entry).

It depends a lot on your use case. I wouldn't say that Go is unconditionally more practical: there are cases in which you just can't use it.

Thaxll|9 years ago

Choosing Rust in a shop where no one knows Rust is indeed a big mistake where in Go it's pretty easy to pick up the language quickly.

pcwalton|9 years ago

My experience is the exact opposite. Most of the people who I work with now using Rust did not know any Rust at the time they were hired. There haven't been any major problems at all.

Do you have experience that shows otherwise?

zigzigzag|9 years ago

Out of curiousity, why were the only choices Go or Rust? The type of thing you describe sounds ideal for Scala, Java, Kotlin, C# ...

fizzbatter|9 years ago

I had to pick something i was familiar with too. We need this on a shorter timeframe (when is that not true? haha), and evaluating a language is hard.

As it is, this was a partial reason against Rust for me. After my time learning Rust, i'm still not 100% confident in my ability on it. My lack of experience and thusly productivity-hit is a real factor i had to calculate. Moreso with the rest of the team.

With Scala/etc i would be learning it new myself, and it was a gamble i wasn't going to take. Especially because our application is not intensive to specific problems that some languages excel in (concurrency, etc).

I'm sticking my neck out choosing a language, so i wanted to be quite sure i make the right choice for us. Well, as best i can - i am new in this shop after all.. i hope i made the right one :)

weberc2|9 years ago

Honestly, unless you need some special library or your team is already familiar with those languages, I would pretty much always recommend Go over those languages. Everything is just simpler, and it's at least as fast as any of those languages.

agnivade|9 years ago

> 3. Package management.

This is being taken care of. A package management committee has been formed and they will come up with a prototype tool in Go 1.8. https://docs.google.com/document/d/1qnmjwfMmvSCDaY4jxPmLAcca...

> 4. Enums.

Doesn't the iota identifier solve it for you ? It pretty much covers all my enum use cases.

masklinn|9 years ago

> Doesn't the iota identifier solve it for you ?

They're talking about sum types (aka tagged unions aka variant record), where you have a proper type with a closed set of variants (which can be checked for completeness by the compiler) and optional associated data (possibly of different types for each variant).

Iota isn't even a step up from C's enums, it doesn't come close to what you can find in ML descendants (such as Rust or Swift for the most recent ones).

greendragon|9 years ago

Curious what they weren't so happy about with Python? Was it purely performance? If so, did they consider PyPy, or at least profile what the slowest bits are so they can evaluate whether to throw everything out or just rewrite the slow bits? Was it the language itself? Not everyone likes dynamic languages, though it's odd they started with it. Did you consider Node at all?

snovv_crash|9 years ago

From my time doing server backend python dev, it is only catching any problems at runtime, everything from missing arguments to typos in variable names that accidentally match another variable, turning your int to a string. Having a compiler catch these saves much time and hairpulling. And having unit tests as a final defense, rather than the only defense, does wonders for my peace of mind.

fizzbatter|9 years ago

Type safety mainly, i think. Performance is a definite concern, but they have a lot of internal applications and the stability of them varies. I offered up that less dynamic languages would provide more speed and reliability to boot.

I know Python got types in 3.5, though i'm not sure if it has Go-like Interfaces (Traits in Rust). If not, i think it really should.

I do firmly believe they'll be quite happy with Go though. Rust, not so much.

lelandbatey|9 years ago

For me, it's the extremely straightforward conventions of Go, with it's straightforward tooling, and it's strong typing.

I "grew up" on Python, wrote a lot of code in it, and love it. But it doesn't feel as cohesive as Go.

As an example of cohesive tool design, let's look at Go package management. In Go, if I want to install a package, I install it with:

    $ go get github.com/pkg/term
Having installed this package, I import it in my code with:

    import "github.com/pkg/term"
Having imported this package, I'd like to read the documentation for it. To do that I use the command `go doc` with the package name:

    $ go dock github.com/pkg/term
Now that I've read the docs, I've got a question about how some particular functionality is implemented. With Go, I happen to know exactly where I can read that code, on my own hard drive:

    $ cd $GOPATH/src/github.com/pkg/term
With Python, I find that I don't have this absolute guarantee of consistency. Usually, packages will have a similar convention, but some require installing with one name and importing with another, and the local documentation viewer (pydoc) isn't installed by default, so I didn't even know about it until relatively late in my use of Python. I've had a similar experience with the rest of Python's tooling: it's as feature complete or better than Go's, but it's not quite as consistent as Go.

RBerenguel|9 years ago

Can't speak for the OP, but when you go full type checking it's hard to go back. Our infrastructure has many pieces in Python (right now I'm rewriting some) but all new APIs are in Go. The amount of trouble you don't even get to fight with type checking is huge. Performance gains are also good in many cases. Slightly more verbosity is a minor price to pay.

stcredzero|9 years ago

4. Enums. My god, Enums. Such a simple feature, but so insanely welcome and useful in Rust.

Enums are so underrated. So many architecture problems introduced by bad coders in Smalltalk would have been entirely avoided with Swift style enums.

detaro|9 years ago

Can you give examples, assuming you are referring to patterns that still are somewhat common? I've seen a bunch of somewhat-connected Singletons used as enums with extra functionality in Smalltalk, is that the kind of thing you were thinking of?

didip|9 years ago

I solved channel without panic by doing channel bookkeeping in the main() entry point.

Wholeheartedly agree with you that channel panics a little too easily.

dilap|9 years ago

i love go, but follow rust with a lot of curiosity. a very interesting language, and much more ambitious than go.

when i've played with it, one thing that was always a big turn-off for me was super-long compile times.

as someone who used to do a lot of c++, i am loathe to go back to a language with long compile times, no matter how good the language...

petre|9 years ago

Have you tried D?

krylon|9 years ago

> Enums. My god, Enums.

Oh yes, please.

Manishearth|9 years ago

A lot of this comment resonates with my experience and views :)

> You'll note that i didn't list Generics. I know that's high on peoples list, but not mine

This is me too.

Been programming in Rust for 3 years, and picked up Go two years ago. I like the language; I like how it feels like "C but safety net". I haven't used it for anything important (course projects a bit), but this is because so far Rust works for almost all my needs and for everything else I use Python. But I'd be happy to use Go if someone asked me to or there was a reason why Python wasn't suitable.

However, generics aren't what I want in Go. I get that a language without generics can get complex and perhaps slow to compile and has other issues too. On the other hand, enums are on the top of the list for me. Especially for the message-passing programs I tend to write in Go. There have been times when I've hacked together an enum system using interfaces and hidden methods but its not great. I've grown to get used to error handling, but I do think it can be improved a lot. Package management was a major gripe of mine but it seems like y'all are fixing that :D

I have not written production software in Go so panic-proof channels haven't been on my radar but yeah, that one makes sense.

> didn't want to make a choice that would cause them to spend weeks/months feeling unproductive.

+1 I have often recommended Go to people who don't have time but want to learn something new. If you want to actually spend time writing software from scratch in week 1 of learning the language, Go is amazing. I recommend Rust often too, but I usually find out how much time they have and/or their background first.

> Rust gives me a peace of mind that Go never came close to.

Again, big +1. For me it is two effects -- one is that Rust feels safer, and the other is that as a Rustacean Go feels wasteful at times. After programming in Rust for that long, losing performance at any corner for no reason irks me. In Rust, for example, most folks will avoid reference counting and trait objects and heap allocation as much as possible. If you see an unnecessary trait object it _feels wrong_. This is a perfectly sensible attitude to have in Rust. For me, it often carries on into Go. But Go loves interfaces and has garbage collection (with good escape analysis, but a GC nevertheless). Every time I use an interface object in Go, it _feels wrong_. It shouldn't. And I've learned to ignore it -- if I'm writing an application in Go; perf probably didn't matter enough for this to matter. (In fact, the odd unnecessary trait object in a typical Rust lib is usually no big deal either.). But, that nagging feeling is still there :)

weberc2|9 years ago

You can also build enums by building a struct with a `Tag` field. This works well and generally has better performance characteristics than using interfaces.