Hacker News new | more | comments | ask | show | jobs | submit login
Rust vs. Go (ntpsec.org)
481 points by ingve on Jan 18, 2017 | hide | past | web | favorite | 580 comments



From ESR's conclusion is this:

> For comparison, I switched from Rust to Go and, in the same amount of time I had spent stuggling [sic] to make even a <100 LOC partial implementation work, was able to write and test the entire exterior of an IRC server - all the socket-fu and concurrency handling - leaving only the IRC-protocol state machine to be done.

I don't think this is an unusual experience. I'd consider myself a journeyman programmer with 16 years of experience and probably a dozen languages under my belt. Learning Go was unique: after 3 days I felt productive; After 2 weeks I sort of looked around and said "is this it?" Go gets out of your way for the most part, and performs as advertised.

For a lot of programmers, for a lot of projects, that's exactly what you want.

I've attempted to learn Rust a few times, and it always feel like I'm moving uphill.

Rust is awesome for a number of problem spaces, so I'm not knocking it. And Go _isn't_ awesome in a lot of ways (vendoring, I'm looking at you). But it feels like there are a lot of core, language-level things Rust needs to improve to attract more non-genius developers.


A friend of mine put it into good words: Go and Rust are two big conclusions, to big follow-ups to the C and C++ world.

Go is an easy, fast, and very parallel language. Go is exactly what google needs, in all regards. It makes sense why Go exists, it makes sense why google is creating a way to migrate from python2 to go. Go is easier than hard C and Go is fast.

Rust is the safe follow-up of C, but more familiar than Haskell, Caml and such. It's a pain to get working, it sucks to program in, it requires so much thought, it's just a bloody fucking fuck. But once rust runs, it works and there's a very very good chance it won't break.

And that's also the reason why we'll probably build firewalls and hardened OS in derivatives of Haskell and Rust, and we'll build Google 2 and Facebook 2 in Go. And in my book, that's a good thing.


> It's a pain to get working, it sucks to program in, it requires so much though, it's just a bloody fucking fuck

What sucks about programming in Rust? What makes it a bloody fucking fuck? I disagree on both these points:

  * Rust enums and move semantics make writing state machines a pure joy.  Far nicer than in C++ or Go.
  * Pattern matching (both fallible + infallible) is very developer friendly.
  * Option<T> and Result<T,E> are totally brilliant.
  * It's trivial to add and version-fix a dependency on an external library with Cargo.
  * Rust's ownership and move semantics help you to write cleaner, easier to understand code.


People who say Rust is hard are probably skipping on reading the really well written Rust book. I'm not a genius in any shape or form, but I am able to write decent Rust apps just fine. I've written an IRC bot in Rust and am working on a file utility now.

It took me longer than 4 days (almost two weeks) to just read the rust book (part time), so yes you're not going to get from 0 to 100 in just four days. If that's your benchmark, how fast you can get going, then you're right Rust isn't for you. But nobody would expect anyone to get up to speed with C++ in just four days.

Rust is a systems programming language with a focus on security and performance, not a play toy for people looking to write small scripts.


> not a play toy for people looking to write small scripts.

IMO this tone or slights against Go will hardly endear Rust to people. Rust's superiority should stands on its own. Not just you I have seen this sentiment many times by Rust enthusiasts on reddit and elsewhere.

Go already has successful companies like Docker, CoreOS etc literally built using Go. There are 100s of companies using Go productively. Very large software like kubernetes, docker, etcd are written in Go and they are not toy scripts.


I encourage everyone in this thread to not take things personally. Rust vs. Go conversations are going to cause a lot of angst on both sides. I also encourage people not to assume or read into comments, where someone states that "Rust is not a play thing", they are not implying that Go is.

For some people who've spent the time with it, Rust is a godsend, but that does not mean that by believing so they implicitly hate Go in anyway.

Go clearly has a lot of people who love it as much as people who love Rust, and because they overlap, there will constantly be a conversation of which language to pick for cases where it looks like both could be used. As such, the learning curve for Rust will always be an impediment, to its adoption, which is sad, b/c in all my experience it's the only language that I've used which inherently answers every core issue or fundamental bug that I've encountered in my career. This is why I'm really excited that the Rust core development team has decided to focus on development experience heavily this year.


I did not mean to imply that Go is a toy language. The Go developers are very smart engineers and they made a great language. Sorry if it came across that way. I was more making a point about putting some effort into learning Rust. I did not mention Go anywhere in my comment but I understand why people thought I intended to slight Go. I didn't.


Adding a quick opinion here: I worked for one of these companies (working with libraries written by all of these) and it's just a matter of personal taste. Docker, CoreOS, Hashicorp, etc. are "successful" (which is hard to confirm for now) because they can gather a lot of open source contributions thanks to the easy Go learning curve. I don't think this would be the case if they used Rust instead. Go is definitely not a play toy and you can build scalable and reliable software with it. The problem with Go in my opinion is also its ease of use, it's incredibly easy to write sloppy/buggy code with panics/data races that become increasingly hard to debug. I've had my fair share of frustrating experiences debugging concurrent code with hundreds of goroutines or investigating random data races on complex distributed systems code. I've yet to see that happening with Rust after four months of using it, the compiler does an incredible job at preventing these.

While Go's frustration often comes from debugging sloppy concurrent code, the frustration with Rust is often about having your code to compile. But once it compiles, it runs beautifully. Some prefer the former, I personally prefer the latter. Also, I think the argument of increased productivity with Go should not be the main argument, because the time you save writing a large program, you will probably lose trying to debug a deadlock (which could still happen in Rust but harder to make happen) or a random data race that the race detector couldn't catch (or in a vendored library).

If I had to define the experience with both languages, Go is the instant gratification option, you feel productive quickly, the language is easy to learn with a forgiving syntax sometimes at the expense of correctness. Rust is the delayed gratification option, frustrating at times, very hard to learn and master but offering strong safety guarantees with an elegant syntax (which is a personal opinion).

Also it's a controversial topic in the Go community but the lack of generics...

Rust has its downsides too, for example the slow compile times or the lack of maintained libraries for some core functionalities but overall, programming with Rust is a much more enjoyable experience once you start getting ahead the learning curve.

One last thing: It is hopeless trying to learn Rust in four days without very good resources/books. I recommend "Programming Rust" from OReilly if you really want to dig into the language. It's incomplete at the time of writing but still one of the most useful resource out there (with the free Rust ebook). My knowledge of Rust dramatically increased after reading it and I felt much more confident writing larger programs.


Go is a language that is used to write on top of things. Rust can, maybe, be used to write the things Go are written on top of.

This sentiment (or slight) is the same as always said of C and C++, that they are used to make real things (systems and infrastructure, foundations), and the sentiment (and slight) is there because there's a lot of truth to it.

Which language was used to write our *nixes, windows, the biggest web servers (apache, nginx, IIS), haproxy, mysql, postgresql, oracle db, etc, etc? C/C++, of course. What else?

Docker is just a front end/abstraction layer for the features already in the Linux kernel (written in C). You can write, what's essentially just automation tools, like this in any language.

I don't think what you write in this post is a testament to any strength of Go. If currently there exist a testament to its strength it must just be the fact that it's used; adoption rate/popularity, although I have not seen the numbers.


> Docker is just a front end/abstraction layer for the features already in the Linux kernel (written in C).

That's actually a perfect example of "systems programming", in the true sense of the phrase. And it is done in Go, because it can be done in Go.


Your point is fair about trivializing the other party, even implicitly.

Docker and CoreOS are not really successful companies...it's hard to say if any of the companies in that space will be successful.


> not a play toy for people looking to write small scripts.

That's actually a reasonable beginner's use for Rust though [0]. It's easy to do and will expose you to enough of the language to be a learning experience, but not so much that you could get badly stuck. And the benefit is that it's a lot easier to share binaries than it is to share scripts that rely on a particular interpreter being installed.

[0] http://www.chriskrycho.com/2016/using-rust-for-scripting.htm...


It's not that easy to accept binaries compared to scripts I can open and see what they do.


You could store them as source files with a hashbang, and make something like "rustrun" which compiles a single Rust source code file and runs the resulting executable? That should take ~15 minutes.

I do wish there was an actual Rust interpreter.


Why? Rust is a systems-level language. It's not designed for scripting. If that's what you want, use Python/bash/{yourFavoriteScriptingLanguage}.


Well, if you can interpret it then its easy to build a repl, and repls are nice.


"being interpreted" in not enough to have useful repl. You also need to be dynamic and introspectable. There are interpreters for C, but it doesn't make it suitable to use with repl.


there's nothing bad about interpreting a systems language. there are C interpreters available, too. i'd argue it'd be a very useful feature since the compile times of rust are one of its biggest pain points right now.


There is a dead project named `rusti` (fails to build on Rust nightly >= 2016-08-01) that can interpret Rust source, and a newer project named `miri` (last commit 3 days ago) that interprets MIR output from the compiler. Several of us in the community, me included, would like a REPL, so I'd keep an eye on these projects to see if they go anywhere.


The big thing for me in this regard is not worrying about dependencies (in a lot of cases). With Rust, when someone asks me for a script that does X, I can compile and ship a binary and they run it end of story. With python, I say "ok, now install python version whatever, then install pip, and then pip install all this stuff, and finally run my script"


I've went through the rust book, unfortunately I learn better through actually trying to do things while looking at the API reference. And the rust documentation is frustrating for that, simply because everything is expanded by default and there's no concise list of functions available.

(In my case, I wanted to print out the name of the executable. Having to convert OsStr to something I can print, I understand - the filesystem encoding can be different from the console encoding. But it took longer than it should to find a path between the two.)


I mean, pressing the button to collapse the documentation isn't very hard http://i.imgur.com/rcI3JG5.jpg

I never had to print an osstr yet but it seems fairly straightforward when looking at the docs? There are three obvious functions one could use, each for different use cases: https://doc.rust-lang.org/std/ffi/struct.OsString.html

  os_string.to_str() // returns Option<String>
  os_string.to_string_lossy() // returns Cow<String>, replacing illegal characters
  os_string.into_string() // returns Result<String, OsString> - String if the data is valid, stays OsString for error handling otherwise


I write small scripts in Rust all the time. It's perfectly suited to it even though it's not an interpreted language.


Mind telling what kind?


Just little things like importing content from JSON files. It's not ideal but cargo makes it pretty easy so it's not that bad either.

I'd be better doing it in another language, I'm doing it mainly to keep getting familiarity with the language.


>>People who say Rust is hard are probably skipping on reading the really well written Rust book.

Do you need to read a really good Go book to be productive in Go? If not, I think the OP's point stands, no?


I'm not sure that's fair.

There are certain languages which are different enough from traditional semantics to need an explanation. While others are similar enough to be "obvious" if you've used a semantically similar language before.

To give a specific example, Powershell. It is semantically a very different scripting language from almost anything else in the market. Extremely well designed and powerful, but you'll want to sit down and read an actual book on how it works to get to handle on it because you won't have the mental map already to do so.

Overall this is why the programming language market is so predisposed for new languages looking and feeling like old languages. People just aren't comfortable stepping too far outside of their comfort zone semantically (even if they'll happily do so for a few new language features and libraries).


Yeah one of the best things about Go is how familiar it is, you can pick it up and run with it very quickly.

One of the best things about Rust is that it's a paradigm change, it takes time to get your mind around it but it is worth it if the language fits your use case.


> People just aren't comfortable stepping too far outside of their comfort zone semantically (even if they'll happily do so for a few new language features and libraries).

I'm not sure that's fair. It seems more likely that people who choose "boring" languages do so because they have things to build, and want to pick a language with a good risk/reward value. Not out of some academic interest or comfort calculation.


I believe he's saying that the Rust book[0] is very good (and completely free).

[0]: https://doc.rust-lang.org/stable/book/


I think that focusing on how long it takes to be productive is a mistake, within reason at least. How well does it hold up once I'm knowledgeable with the language matters a lot more. If it takes me 4 days or 3 weeks to be productive, ok, that's fine, but it needs to be a good solution once I'm there, because I'll spend years using it potentially.


The really, really important thing about the time it takes to be productive in a language is selling the language to other people. If it takes 3 weeks to learn enough Rust to make interesting projects, then it's going to be absolute hell convincing a team of largely ambivalent co-workers to use Rust, because 3 weeks is equivalent to months of weekend tinkering. You're restricted to using Rust either when it has mass adoption, it interests your devs, or the domain needs provable safety guarantees.

On the other hand, if someone can pick up Go and produce a side project with two weekends worth of messing around, they're much more likely to be supportive of the language.

Obviously this isn't ideal, but I don't know how you'd fix the problem.


Do you really want people on your team who think that having to read a free book and investing 3 weeks to learn a new language is too much of an effort?


Firstly, when the initial impression of a language is that it's not fast to code in, people seem to assume that it will remain unproductive in regular use. This is clearly the case with ESR, and is a problem that's relevant to the Rust community in general.

Secondly, as per the Rolling Stones, you can't always get what you want.


Maybe they are spending those three weeks learning something else. There are thousands of things that "every programmer should know" and thousands more that "every human should know", and you will never have time to learn them all.


It may very well be out of your hands. I wasn't able to convince my coworkers to use Kotlin for an android app, even though at the end of the day I was the only one coding it. And Kotlin takes a few hours to pick up if you know Java.


Sometimes you don't have a choice. You don't want those people but they are forced on you.

It's strange the short term focus that is accepted in what are nominally professionals.


If you've come from an ML background you'd need a good book to be productive in Go, and jump straight into Rust. Go is more C-like which makes it easier to pick up coming from C, not absolutely easier.


I believe Rust is hard, and I certainly did not ignore the Rust book. I haven't really read it in about a year though, since abandoning this project http://joelmccracken.github.io/entries/a-simple-web-app-in-r...


Do you have the sources for your IRC bot somewhere? I have a bot written with Ruby in a quite big channel and I have this tendency of writing it with a new language every couple of years. An IRC framework with Rust and Tokio would be fun...


I have the source, but it was more a process of me learning than something intended to be used by others. The source code is not online. It can read from a toml config and connect to multiple servers and respond to commands. It's pretty cool. I had it up at Github for a bit, I can put it back up, but this is an anonymous account so I wouldn't share it from my Github. I'm not sure how to share it. :\


put it up on an anonymous gist, if you want


Instead of writing an IRC bot, I have written an IRC client in Rust here: https://github.com/FreeFull/ircclient . Beware, I haven't put much effort into making the code clean or well documented. Writing an IRC bot would be easier than a client, because you wouldn't have to deal with reading from stdin, and you'd be able to keep everything single-threaded. The IRC library I use doesn't have any tokio support yet.


I was with you until your last paragraph:

> Rust is a systems programming language with a focus on security and performance, not a play toy for people looking to write small scripts.

If you're suggesting Go is a play toy, you're wrong.


I could see how this statement could be construed as inflammatory, but I don't think he was implying that Go is a play toy. He was implying that you can't make a judgment on the language based on making play toys (the toy IRC client mentioned in the article).


Yes this is correct. I do not in anyway think Go is a toy. It's a fine language. I love kubernetes and it's written in Go. A lot of people took that statement to mean that I thought Go is a toy and I didn't mean that, so I apologize.


I'm learning rust now, this isn't even my first attempt. I know Go really well and C reasonably well and I can say Rust is much harder. If you want specifics: I can tell you that I've found changing a type from HashMap<String, u64> to HashMap<T: Hash+Eq, u64> within one of my projects to be extremely hard. I've read the rust documentation, and had to resort to reddit several times. I'm stuck dealing with mutable/immutable borrow issues to accomplish relatively simple tasks like searching for entries and removing them. In go these issues don't exist. I'm sure I'll figure them out eventually, and I'm a fan of rust, but it's a confusing frustrating grind.

My understanding is that the rust team has put 2 priorities ahead of everything else: zero cost abstraction and safety. Given those constraints they've done a fantastic job in the area of ergonomics, but I don't know if it will ever be easy. Maybe rust is as easy to use as it can be.


Have a link to your code?

> HashMap<T: Hash+Eq, u64>

I can take a wild guess that T is a generic type defined on either the method, struct or impl in which this HashMap is declared. It's on the method/struct/impl. You don't necessarily need the type bound on the struct, but you do on the impl declaration:

struct MyStruct<T: Hash+Eq> { map: HashMap<T, u64> }

impl<T: Hash+Eq> MyStruct<T> { pub fn insert_something(&mut self, my_key: T, my_int: u64) { self.map.insert(my_key, my_int); } }

try it here: https://is.gd/RwUpQx


Please don't hesitate to ask for help on #rust-beginners. I was in your situation last May, and hanging out on IRC was an excellent experience. Rustaceans, generally speaking, will bend over backwards to help you work on your project.


The biggest hurdle people seem to have with Rust is that it makes you very aware of the difference between stack and heap allocated data. For someone coming from a garbage collected language (or even in some cases C) that's a pretty big hurdle to get used to. I've found Rust to be amazing to work with, but it does feel like a struggle sometimes when all I want to do is allocate some structs without having to faff about with Box types.

Honestly I feel like peoples biggest issue with Rust is that they're trying to write things in Rust that don't actually need Rust. They want the ease of use that you get with a garbage collected language and then get annoyed when Rust forces them to worry about allocations and cleaning up after themselves. C++ should have the same issue but its been around long enough that most of those details have safely been wrapped behind STL and other abstractions to the point where most people don't need to worry about them. Perhaps in time Rust will reach that point as well and everyone will be using the equivalent of Boost that abstracts worrying about whether something is a Box<ARC<Foo>> behind a nonthreatening layer of macros.


Go has the same distinction (stack vs heap), but the GC means you don't need to care about the lifetime of the data.

To your other point, it saddens me that Rust isn't well-suited to higher level applications. You need very strict performance requirements before it makes sense to pick Rust over Go (you can squeeze a lot of performance out of Go, and even optimizing Go is friendlier than writing naive Rust IMHO).


I think this might depend a lot on how you approach Rust. I find the functional/procedural mixture to work nicely, and find I'm much more productive in Rust than Go (or Python or Java, modulo available libraries). I'm writing web services and code generators, so not particularly low-level code.

I've written a decent bit of Go, and I understand why a lot of people like it, but I don't think it's inherently better for application development.


Maybe, but I think your case is atypical. I came from a C++ background, so I'm well-acquainted with navigating a huge feature matrix and thinking about memory management, but I'm still much more productive in Go simply because the feature matrix is always very small and I only need to think about memory management in hot paths.

I want to be comparatively productive in Rust, I just never seem to get there because of the huge volume of decisions (even if the decisions are mostly easy) that need to be made for even straightforward code units. I think what I really want is Rust lite, or Go with generics and abstract data types.


I could absolutely be atypical; prior to using Go and Rust as my main tools, I primarily wrote Scala, and I'm sure the functional and type-driven approaches have left deep grooves. But that would still be a way of approaching working with Rust, wouldn't it?

None of that is to make a value judgement, just an observation; but I would still argue that the language can be used in a highly productive manner. Nonetheless, it's perfectly reasonable to argue it's not worth adapting to it, especially if Go is already solving your problems and keeping you productive. I switched because Go's design doesn't seem to fit me very well, and I continually tripped on issues that I don't encounter in Rust. (So yes, perhaps I'm quite odd.)

I do understand the want for a Rust-lite; the thought has crossed my mind more than once. So far, Swift seems closest in many ways and may get close once the Linux/cross-platform story starts looking good.


> I could absolutely be atypical; prior to using Go and Rust as my main tools, I primarily wrote Scala.

I'm not saying Rust is easy, but I have also a long Scala and some Haskell background which made it easier to learn Rust. For me the hardest part was that I've never really wrote any proper C++ or C, although I could read them quite good. Refereneces and RAII caused some gray hairs, the type system not that much.


> I just never seem to get there because of the huge volume of decisions (even if the decisions are mostly easy) that need to be made for even straightforward code units.

Well this is true for all languages! You need to think hard for every decision you take with languages like Ruby or Python. Things like how will this look like after a year, will the junior developer be able to extend it, will there be data races and so on.

Rust just enforces you to do this. And it makes life much easier when you don't need to worry about some silly mistakes you made, because the compiler will catch them.


>Well this is true for all languages!

That is unfortunately not true enough.

Rust has a brilliant solution for some really burdensome issues that C++ developers have. A lot of C++ code tends to make assumptions about ownership and lifetimes that are not formally spelled out anywhere in the code. It's all over the place, it increases cognitive load, it makes the code brittle and less modular than it could be. Rust fixes that without negatively affecting performance whereas all C++ workarounds like shared_ptr and defensive copying are incomplete and/or have a negative performance impact.

But Rust also insists on enforcing its ownership rules between variables within the local scope where it doesn't help much and feels needlessly restrictive and convoluted. I can't say I know Rust well enough to tell whether working under these restrictions can become second nature and stop being a burden.

Also, users of garbage collected languages don't have many of the ownership and lifetime issues that C++ developers are forced to think about. Especially where objects are immutable, ownership is not a concern at all.

So I think Rust is an answer to one question: How can I have detailed control over resources like in C++ without inheriting all the hazards of C++ as well.


While I agree that programming necessarily involves a lot of decisions, I hope we can agree that there is a spectrum of complexity among programming languages. My point is that the likes of Rust and C++ introduce a huge volume of decisions that wouldn't exist in a simpler language, like Go. That said, if you think all languages are as complex as C++, I'm not sure we can agree on much...


> Go has the same distinction (stack vs heap)

No, Go does not have that distinction. This is even in the Go FAQ: https://golang.org/doc/faq#stack_or_heap


Yes, it does have the distinction. From your link:

> When possible, the Go compilers will allocate variables that are local to a function in that function's stack frame. However, if the compiler cannot prove that the variable is not referenced after the function returns, then the compiler must allocate the variable on the garbage-collected heap to avoid dangling pointer errors.

As you can see, Go uses both the stack and the heap; however, unlike Rust, it doesn't require users to know where their variables live to write correct programs.


I believe, the distinction that orclev mentioned was that in Rust, you specifically have to be aware when something is allocated to the stack or heap, but in Go, you don't have to since the compiler deals with it. Obviously Go allocates to both stack and heap, it's just that the user doesn't need to know which.


Fair enough. I simply meant that Go's semantics let you reason about what lives on the stack vs what lives on the heap much like you can in Rust (though in Rust it's clearer still because there is no escape analysis at all). While you don't need to know from a correctness perspective, you can reliably reason about what will live on the stack vs heap in a way you can't in, say, Java.


No, you can't really reason properly about what lives on the stack. As the Go FAQ say, you can safely assume a variable will be allocated on the stack if you never take a reference of it, but that's not always clear as it seems. For instance, if you call a method on your variable, you can't immediately know by looking at the method whether the receiver is by-reference (pointer receiver) or by value. If you're using a pointer receiver, the variable's address may be taken, so the compiler has to perform escape analysis.

Once you take the address and escape analysis is performed, you've got no way to easily reason about what's on the stack vs. what's on the heap, without delving into the gory implementation details which may be changed in future versions.

Since Go isn't C++ and doesn't have a huge and complicated standard that guarantees compiler optimizations, you can't really reason about what's going on.

This is essentially the same situation as Java, although the Hotspot JVM's escape analysis is probably less 'basic'.


That is one reason to choose Rust, but that is far from the only reason to prefer Rust over Go. I've found that algebraic data types and pattern matching have become irreplaceable parts of my programming toolbox, to the point it is painful to work without them. Rust also has a powerful macro system that Go cannot match.

This is not to say that Rust is better than Go in every way, but performance is far from the only facet when I compare the two.


> The biggest hurdle people seem to have with Rust is that it makes you very aware of the difference between stack and heap allocated data.

What do you base this belief on? I'm very comfortable with C and assembly. I am very certain I understand the difference between stack and heap allocated data, but I've found lots of other hurdles in learning Rust.


What were the hurdles that you encountered?

One difficult aspect of Rust relative to Go must surely be generics and trait bounds. This is something it shares with Haskell and Scala. For people coming from those languages (like myself) the stack/heap thing is the most salient.


> What were the hurdles that you encountered?

> One difficult aspect of Rust relative to Go must surely be generics and trait bounds.

Conceptually this isn't bad at all. I understand the value and mechanism of specifying the required traits for types to a generic function. However, I just posted an example elsewhere in this discussion where there simply isn't a trait for the method I want to call. That was certainly a hurdle for me, and the workaround wasn't satisfying.

I mostly understand the semantics of borrowing mutable vs read-only references, and I'm pretty sure I understand the purpose and value of the concept. However, specifying lifetimes is completely different than any other language I've ever used, and I've used a couple dozen languages over several decades in my career. I haven't gotten past that hurdle yet.

> the stack/heap thing is the most salient.

I'll stick with my statement that I understand the stack and heap well enough (from a C or C++ point of view), but composing Box, Cell, Ref, RefCell, and friends is a non-trivial hurdle.


Theoretically yes, for a C/Assembly programmer you're aware of the difference between stack and heap allocated data, BUT the cognitive load is different because those languages don't differentiate them in a particular fashion, it's all just pointers and offsets at the end of the day. Rust on the other hand, because of the Box type makes it very explicit where exactly some piece of data lives. It's also relatively uncommon to stack allocate any kind of larger structure in C or assembly, almost by default that kind of thing is done in the heap, where Rust makes it really trivial (default even) to do so on the stack even though that's rarely the correct decision.

Depending on your background there probably are various hurdles to overcome in learning Rust, I'm not discounting that, but the most uniquely Rust one is the stack vs. heap issue, particularly since that combined with issues around shared state are the sources of most of the headaches you'll run into when fighting with the borrow checker. Pretty much every other feature of Rust is similar enough to another language that if you have experience elsewhere you won't find it particular troublesome. References are easy to understand if you know C/C++ (and to a lesser extent C#). The type system is easy to understand if you know Haskell or Scala (and to a lesser extent JavaScript/Python). The generics system is pretty much the same as Java, C#, Haskell, Scala, or C++ (and probably others).

Rust IS a complex language, but it also provides a unique feature set and has some fairly unique goals. IF you need or desire the features Rust provides, pretty much ONLY Rust is going to get you what you want. C, C++, or Assembly potentially can deliver the same functionality, although it's questionable whether their ergonomics will be better or worse than Rusts for a given task, and they almost certainly will be more bug and error prone. Rust lets you have all the fine grained performance tweaking (or just plain low level access) you can get with C/C++/Assembly, while also giving a strong peace of mind that your code is correct and error free. It's also arguably easier than C++ although that comparison is a bit Apples and Oranges, C++ has a great many years of tweaking and experimenting behind it so things like Boost go a long way towards providing something like a most optimal (from a ergonomics standpoint) C++ experience that ignores all the languages pitfalls and sandtraps. Rust on the other hand has just barely hit 1.0 and the community hasn't even discussed, let alone come to any kind of consensus on the best way to tackle various issues yet. Rust will get there in time, but it's still early in the process and it shows.

Bottom line, if you need the features Rust provides, but aren't comfortable with how young the language is and want more hand holding, C++ (or maybe C) is probably the language you want. This isn't to say Rust is a bad language or you shouldn't use it, I think learning and using a new language is reason enough and the certainty Rusts type system and borrow checker provide is a very strong argument in its favor, but you need to be aware of what you're signing up for when you make that decision.


> the most uniquely Rust [hurdle] is the stack vs. heap issue

I can only speak for myself, and I politely disagree. So far, explicit lifetimes (both the syntax and the concepts) seem like the most uniquely Rust issue.

> [if you] want more hand holding, C++ (or maybe C) is probably the language you want.

Heh, if you think C++ holds your hand, I'm not sure we're on the same page. What I know of C++ was hard-won after reading many books and fighting with the compiler for many years.


Sorry, it wasn't exactly clear from the context but by hand holding I meant more in terms of the resources available. C++ by its very nature has tons of great libraries, tools, and various kinds of documentation. If you can think of it, someone has most likely written a tutorial about how to do it in C++. Additionally there have been entire forests worth of books written about various facets of C++. Rust in contrast has few libraries, fewer tools, and little to no documentation for most of what it does have. This isn't to say Rust is bad, it's simply new and building up documentation takes time.


> [...] Rust forces them to worry about allocations and cleaning up after themselves. C++ should have the same issue but its been around long enough that most of those details have safely been wrapped behind STL and other abstractions to the point where most people don't need to worry about them.

Also, C/C++ comes with the venerable "kill the process GC" that makes it easy for beginners and other mortals to feel good about code even when it not strictly is good. Rust is all about making that kind of quality mandatory.


> Rust is all about making that kind of quality mandatory.

This is why I think the best characterization of Rust is "an Anti-Sloppy Programming Language".


Well, let's see. I was following rust's development for a while, but waited until 1.0 when it seemed it might be kind of stable. All that trivial and obviously correct code turned out to be a kind of wrestling match with the compiler. Far from a pure joy, it was rather frustrating. Set it aside for a while, but then came back recently figuring maybe I'd take another look but instead of starting from scratch I'd pick an existing project and poke around with it. First project I looked at had a requirement that one use the nightly compiler check out, but not just any nightly, specifically the one from last Tuesday. Then I looked at another project, and this also required nightly, but this time it was the one from the Thursday before last. Fuck. This. Noise. I'm not learning a language where every project requires a separate compiler.


That's basically an artifact of the combination of Rust's immaturity and its conservative stability policy - once a language feature or API is supported by a stable compiler, it needs to keep working more or less forever. So a bunch of features still require nightly, so some projects target nightly, and the big ones may pin to specific nightlies. But the amount of breakage is vastly lower than it used to be (especially pre-1.0), and even in the year-and-a-half since 1.0 a lot of different bits have been stabilized, so that sticking to stable Rust no longer feels like being handcuffed. The biggest unstable feature left is compiler plugins, which should get a rudimentary version stabilized Real Soon Now. With that and maybe specialization in place, there should be little reason for projects to stick to nightly.


> wrestling match with the compiler

I went through a similar experience in Haskell. It is worth going through until you get to the point where it is no longer a wrestling match, because then you are just as productive but any code you write is checked much more thoroughly than in a more weakly typed language.

> Fuck. This. Noise. I'm not learning a language where every project requires a separate compiler.

I guess just wait until it stabilises then.


> I guess just wait until it stabilises then.

Which is when? 1.15? 1.50? 2.0?

What was the point of that whole "1.0" thing?


It's right now, with 1.0. Just don't use libraries that require nightly. Libraries are not the language. The language is stable. If your requirement is stability, then use the stable version of rust with stable libraries. That's what I do.


Are you unfamiliar with semver and the promises it makes?

This is a rhetorical question, as there's no way you've been around here so long without knowing what it is. For everyone else, however, the notion is that code written for 1.0 will continue to work for additional 1.x releases. It says nothing about iterating on not-yet-baked features--that's what nightly is for, and as they become baked they become part of 1.x releases. They're not breaking backwards compatibility; the major version remains the major version.


Touche! I remember a place where I worked where the first version was 3 because they figured customers wouldn't trust a version 1. I guess these guys went for version deflation.


I'm totally fine learning the borrow checker. It's a investment I see value in. Even if it's just in me learning about solid rules for sharing variables. But that nightly shit you describe is scary as fuck and makes me very concerned about the future of the language. Post 1.0 this shouldn't be an issue!


There are a couple of fairly popular libraries that were nicer to use on nightly Rust (serde and diesel), but could otherwise work on Rust stable. The "nicer to use on nightly" is going away with the next stable release of Rust. Other than that, the vast majority of the ecosystem is on Rust stable.


Unfortunately "the next stable is the one you want" has been the story for quite a while now. Since this is ostensibly a thread comparing with go, I'll note that their story is more like "the next release will be better, but don't wait for it".


> Unfortunately "the next stable is the one you want" has been the story for quite a while now.

I don't recall anyone ever saying this, where "the one you want" means "the one that supports procedural macros".

We have had this discussion for a very long time, and it has always centered around serde and diesel. Those have been the biggest nightly users since 1.0, and probably before then.


> Unfortunately "the next stable is the one you want" has been the story for quite a while now.

Not really. serde and diesel have been more convenient on nightly for a long time now. It is only just now that we can say the "next stable" because that's when the particular feature they need will be stable.

> I'll note that their story is more like "the next release will be better, but don't wait for it".

I can't wait to use Go's `sort.Slice` function[1], but I'll have to wait for the next stable release.

Seriously though, I'd say the key difference between Go and Rust on this venue is that while Rust is stable, there's still more stuff we'd like to add. To that end, we encourage experimentation by making the acquisition of a nightly Rust compiler really easy. This naturally leads to people experimenting with nightly and publishing crates that either use it or require it. This is a necessary part of experimenting with new features and getting feedback.

Go's scope for experimentation is much smaller. Their changes are much more incremental and conservative. It's a really nice benefit of using Go.

My point is: this is a trade off and it should be presented as such. In Rust land, it requires that you---the user of Rust---ignore crates that require nightly or ignore features of crates that require nightly.

[1] - https://tip.golang.org/pkg/sort/#Slice


Yeah, I constantly ran into this when parsing JSON. I hope you are right and it will be over soon.


I consider libraries using a nightly Rust to be proofs of concept, not actually usable tools. This might be possible in the future, let's push these features to the stable Rust and maybe we could have this kind of libraries in the future.

I always use stable compiler. Never had a need for nightly.


The main blocker on stable was a feature called "custom derive" or "macros 1.1" which will be stabilized in the 1.15 release in February. At this point any library tied to a specific nightly is using some extremely cutting edge features.


It's not really scary.

Very few projects require nightly features and of those that do, most of them will work on stable in just a couple of weeks!

Plus you can easily switch between versions with rustup!


I feel like every other crate I've run into claims to require nightly.

In reality, they often work perfectly fine on a reasonably-modern stable, and simply haven't updated their documentation and READMEs to indicate "oh yeah, this nightly feature is now in stable, so just use a stable that's newer than version so-and-so".


Please, if you see this, file an issue against the project, so we can clearly mark those crates as working on stable.


> most of them will work on stable in just a couple of weeks!

Until they don't again because clearly the authors have no interest in being on a stable compiler release.

> Plus you can easily switch between versions with rustup!

I don't think people are complaining because switching versions is hard. Just unnecessary, risky, and yes kind of annoying.


> Until they don't again because clearly the authors have no interest in being on a stable

This is false, most uses of nightly are because of macros, which will be stable in the next version of Rust.


> Until they don't again because clearly the authors have no interest in being on a stable compiler release.

Then use an older version of the package. Unless the package in question was relying on some "nightly" feature that never made it into stable, then you should be fine.


1.0 was what, 1.5 years ago?

I'm not convinced the problem will just go away. Not as long as people are obsessed with the new hotness language features that require a compiler from the tuesday before last.

Cargo also makes me wary. How do I know what I'm choosing is any good, and if it is will it still be around next year. Nature of 3rd party libs I guess.


Cargo doesn't require that you use things from crates.io without vetting them. It's possible to, say, write everything yourself, or only use specific crates that you've decided are good (whether that's by reading the source, or only using the ones published by the Rust developers, or some other criteria) and even go as far as explicit pinning to specific versions of them in your manifest (although this isn't particularly necessary: Cargo pins versions by default, and won't upgrade without you explicitly asking for it).


The problem really is that you have to vet them at all. With golang's standard library, it's all going to be supported to some degree. (I believe the same happens with rust's standard library, it's just much smaller.)

Basically, some people like having a curated library set; others do fine without and are happy to find things to use. The former set doesn't like rust as much in its current state.


We have the Rust nursery as a curated library set.


The crates published by the Rust developers themselves seem quite similar to the standard library for this purpose. (But, yes, things like this are not as discoverable as they should be; it is one of many things people are working on.)


> I'm not learning a language where every project requires a separate compiler.

All the libraries I have needed for my projects in Rust support stable. Maybe shortly after 1.0 some people were not playing by the rules yet.

The only exceptions I've found are some truly elite macro things and stuff to support code generation.


Was this project rocket.rs by any chance?

Very few projects require the nightly compiler and fewer still a specific version of it, but rocket is highly experimental and kind of a special case.


The simd crate required nightly the last time I tried.


I talked to burntsushi last week about stabilizing SIMD in Rust, and he says he's got about to release an RFC very soon. :) Due to this, I'm hopeful that stable SIMD will land this year.


Thats great news!


Welcome to the downside of version pinning. Now that we have version pinning, developers don't need to make versions forward compatible. Misery is when one program needs packages which use different pinned versions of something. There's probably some namespace hack to allow that.

This is compiler version pinning. At least .toml files don't support that. Yet.


Do you really expect any project to have nightly builds that are forward compatible? The whole point of regular development builds is to have a place to test and do experiments... which is pointless if every experiment needs to be supported forever.

The "namespace hack" in Rust is having the "stable" namespace: a compiler that rejects all unstable code (and is forward compatible), so that people don't accidentally get pinned.

If someone wants to pin to a specific development version (of anything), there's nothing the project can do about that, other than being closed source and only publishing stable artifacts. Tooling like rustup that makes it easier to pin means that people who choose to experiment can still use stable by default (easy to switch back) and that others can easily collaborate on the experiments, both of which seem like strict positives.


Perhaps another incarnation of the rule that work expands in response to productivity improvements. You can improve efficiency, but instead of saving work people will find more ways to create new work.


How does move semantics make writing state machines better? I can understand powerful enums, but I'm not familiar enough with move semantics to envision what you are referring to. Do you know of an example available somewhere?


I wrote you a little example but i think you need to know rust to really understand it :(

https://is.gd/FlBCJ0

you cannot uncomment the comments in the main function and use it wrong, the compiler prevents you to compile this code.

these two sections from the book may help understad this

https://doc.rust-lang.org/book/ownership.html

https://doc.rust-lang.org/book/references-and-borrowing.html

if you're more into videos this may help

http://intorust.com/tutorial/ownership/

http://intorust.com/tutorial/shared-borrows/


Nice example. I was learning Rust over Christmas so I see how it works. But one issue I have is that from the outside it's not obvious that your `close()` method will take ownership of `self`. I'd probably prefer it if there was some (compiler-enforced) convention that made it obvious to clients that `close()` would consume `self`.

As it stands, Rust code can break the principle of least surprise, since what happens in a method can affect the associated instance variable (in this case `open_socket`). A user could easily write several lines of code and only discover that `open_socket` has been moved into a method at compile-time.

I found myself having very small coding-compile iterations when writing in Rust to catch issues like this (but that was also undoubtedly due to the fact I was learning).

Is there a pro tip to avoid this issue?


> it's not obvious that your `close()` method will take ownership of `self`

I'm in the middle of reading the Rust book right now, so this may be dumb, but... Isn't this exactly the point of using a naked `self` instead of the reference `&self`? If it's not a reference, `self` is consumed by the method. At least that's my current understanding

If you mean that the one-character difference is very small for such an important semantic distinction, I might agree.

As for the state machine example, I like it a lot as well. It's very similar to the idea of "making illegal states unrepresentable" from the OCaml world: http://fsharpforfunandprofit.com/posts/designing-with-types-... (This particular post is F#, but the idea is general.)


> Isn't this exactly the point of using a naked `self` instead of the reference `&self`?

Yes it is, but you only see it's a naked self if you look into the method. From the outside there's nothing in the name to indicate that it doesn't take a reference. So potentially it means that for every method call you need to check that it takes a reference to `self` instead of ownership. This would be especially annoying with third-party libraries.


The alternative in other languages is the error (using a socket that has been closed/resource that has been semantically moved away) is only found at runtime. The compiler flagging a mistake like this exactly the point of having the compiler and is a great example of how Rust helps defend against mistakes. Compiling early and compiling often is a great way to develop, IME.

In Rust, I personally don't think about moving vs. not until the compiler tells me I have to, because it usually doesn't matter. Rust previously required moves to be explicitly marked, but it wasn't worth it in practice: http://smallcultfollowing.com/babysteps/blog/2012/10/01/move... (see "Wait, doesn’t this make it hard to know what your program does?" in particular.)


Can you explain the purpose of your '_unconstructable'?


Sure. It is a private field(no pub) preventing you from constructing that type with a literal. It is of type "unit" '()' so it does not add up space for the type. "unit" has only one value that makes it "zero-sized". because i did not supply an "constructor" to that type it is not constructable outside the module and can only created by types in the scope of the enclosing module. Its more or less a private constructor known in C++/Java ... etc.

Steve Klabnik has a more complete write up regarding this http://words.steveklabnik.com/structure-literals-vs-construc...


If a struct is public, and all its members are public, you can just create a new instance of the struct with braces (in that case, it would be `OpenSocket { data: 12 }` - or whatever value you like), not what is necessarily correct. By having a hidden zero sized struct member, you need to use the library's api to construct them.


It is basically a hack to export the type but not the type constructor. It stops people from doing things like

    let new_socket = OpenSocket { port: 12 };


This is really cool, thanks!


Example: http://insanitybit.github.io/2016/05/30/beyond-memory-safety...

TL;DR: compile time checking that your state machine transition is correct.


Thanks, I'll take a look.


It ensures that you consume a state at most once. You can kind of visualize state 'moving' through your functions - going in through the arguments and out through the returned value.


Seriously, you are arguing with emotions, not arguments. It's pointless.


All but the last point will make your code easier to understand and program with. It's not an appeal to emotions at all - those are exactly the features I liked about Rust, and wish more programming languages had.


I don't talk about his points at all. I'm talking about shit like "it's just fucking fuck" - it's emotion, not an argument.


He was quoting the parent comment.


I would say that "it's a pure joy!" is the same thing in reverse. The parent and grandparent are both simply comparing subjective emotions.


No, he's listing useful features.


GP meant to say GGP is arguing against emotions, not arguing using emotions. He's accusing GGP's detractors of emotionality, not GGP.


"Two big conclusions" is a good way to put it.

I would phrase it like this:

• Go is a useful, safe subset of C (with a rather expanded libc.) Go lets you do fewer things than C (e.g. hard real-time things; processor-optimized things) but for most people, they don't need those things, and delving into them was what seeded the bugs into their C/C++ projects in the first place, completely unnecessarily.

• Rust is a formalization/codification of idiomatic C++. It's not a subset; it's a rethink, like Plan 9 is a rethink of Unix. You can do everything in Rust that you can do in C++, and the compiler will catch (nearly) all the bugs you would have left in the equivalent C++ project.

In other words, Go says "you don't need this"; Rust says "if you're so sure you need this, let's ensure you do it right."

For any given piece of code, either Go's assertion or Rust's assertion could be "correct." You either need the things only Rust lets you do, or you don't.

For a large codebase, though, I expect the answer is actually nearly always going to end up somewhere between. Big apps or libraries always have that one part that does heavy lifting and has to go fast, but go fast correctly; and 99% that doesn't.

Thus, I envision the future of many "systems software" codebases to look like this:

1. (1% of LOC) an "engine" library, written in Rust, which compiles to a C-ABI shared object;

2. (3% of LOC) an "engine wrapper" library, written in Go, which imports the "engine" object using Go's C FFI and then exposes a safe Go API to it—and also, the tests for the engine library, written in Go because it's easier;

3. (96% of LOC) the app itself, written in Go, which calls into the "engine wrapper."


> Rust is a formalization/codification of idiomatic C++

Idiomatic C++ favors values and copy semantics. Rust favors references and move semantics. C++ has function and method overloading, and Rust does not. There are so many places where Rust chose exactly the opposite of "modern" C++ that calling Rust a codification or formalization of C++ makes no sense at all to me. Rust has some great features, and C++ has a lot of warts, but Rust is definitely not an improved C++.

Rust seems like a reaction to problems in C++, but for many design decisions I think they erred on the side of doing the opposite of C++ and ignored the things C++ does well.


> ignored the things C++ does well

What are you looking for that Rust is missing?


I wouldn't use the word "missing". I believe several of the Rust language designers are very familiar with C++, and to say that something is missing implies it was an oversight or an accident, or maybe they'll get to it later. Instead, I believe they don't value some of the powerful features in C++ and ignored the fact that other people find those features very valuable.

Here's an example that has bitten me several times now. Try and implement the following C++14 code in Rust:

    template<class T>
    struct Something { T a, b; };

    template<class S, class T>
    auto operator *(const S& x, const Something<T>& y) {
        return Something<decltype(S() * T())> {
            x*y.a, x*y.b
        };
    }
There are ugly workarounds, and I'm certain they have their reasons, but it's so much simpler and cleaner in C++.


Bradfitz (a member of the Go team) has said he would love to see OS / kernel level stuff done in rust and every thing above it in go.


> You can do everything in Rust that you can do in C++, and the compiler will catch (nearly) all the bugs you would have left in the equivalent C++ project.

This is a bold claim, IMO somehow misleading. To "do everything you can do in C++", you have to use the unsafe part of rust, where you essentially lose of the compiler guaranty. Second and maybe ever more importantly, rust never promise the remedy to "nearly all bugs", the universe of bug preventable is a well-defined limited set.


I feel like there are lots of better languages to use than Go if you're just going to call into Rust for anything performance-sensitive. There are dozens (if not hundreds or even thousands) of other languages out there with C-compatible FFI and/or APIs; why use Go?


Isn't calling into "C" (or Rust, in this case) expensive in Go because it seeds to switch stacks?


Expensive in Go, but not in Rust.


I am aware, but the parent of my post suggested writing the "engine core" in Rust, and the other 99% in Go. Does using something that isn't Go for 1% of an application to do the "heavy lifting" and be "fast" really make sense when there is a significant cost that must be paid whenever moving between that core and the rest of the application? I imagine such a situation would need a lot of benchmarking to make sure that the end result is actually faster than writing the entire program in Go.


Imagine something like NumPy. Go code to play with numbers; Rust code to actually do matrix multiplications et al. Since the "hot loop" of such code would be in the Rust part, not in the Go part, you don't see much overhead.

Or, consider Firefox. Firefox's rendering engine will soon be Servo (Rust). I presume that its networking and its Javascript engine will eventually go the same way. Eventually, the only stuff left written in C in the Firefox codebase will be "glue code": business-policy logic that doesn't do heavy lifting, and doesn't need speed. In other words, the exact type of code where Go's "you'll be safer without this" assertion applies. Firefox as Go + Rust: why not?


Sorry, I thought you were asking. I agree, I don't think it makes sense.


In Go 1.8; due this month, cgo calls got half as expensive. They all ready are OK as long as you are smart about them.


"Expensive" in this case is measured in tens/hundreds of nanoseconds, so it might be okay as long as your Rust code is long-running.


> It's a pain to get working, it sucks to program in, it requires so much thought, it's just a bloody fucking fuck.

I could not disagree more! Rust is a great joy to program in. I would rather program in Rust than any other language.

Rust is challenging for many people to learn. That is very different.


Language design is about trade-offs:

The borrow checker and ownership model is a huge tradeoff. Even those very experienced in it still fight it from time to time. Go made a different trade, they chose GC. And GC _is_ easier. But it has computational overhead (well today anyway).

Consider the cascading implications. Rust has a trait for Copy vs. Move types. This means every type has an implicit, and unknown behavior, on certain assignments:

    let v2 = v;
    println!("v is: {}", v);
You can't actually tell me whether the above code compiles without consulting other parts of the code. That's not the case for Go. It is thus simpler and more intuitive to program in.

But it is a trade-off. Go as a language must have a GC. It will never be suitable for certain tasks as a result.

Go also has less compile time checking. This means faster compiles, quicker dev cycles but less compile time safety. Go chose easier here again. These trade-offs are what make rust seem "difficult" in comparison to go. I don't know if you can ever "fix" them.


I am a member of the Rust language design team, I am very aware that language design is about trade offs.

I find the ownership model has non-performance advantages in that I find it provides strong design pressure to factor my system well. I would not describe my experience as 'fighting' the borrow checker - I feel positively when the borrow checker sends me back to the drawing board, because it teaches me things about my system I had not properly comprehended.

(There are areas where the borrow checker is overly conservative, but I never hit these in practice.)

> You can't actually tell me whether the above code compiles without consulting other parts of the code. That's not the case for Go. It is thus simpler and more intuitive to program in.

I'm sorry this is just silly. Here's some Go code:

    v2 := v + 1
You can't actually tell me if the above code compiles without consulting other parts of the code, because you don't know if `v` can be added to `1`. In Rust, type inference is only local to a function, so you will be able to determine from an actual compile-able code snippet whether or not that use after move is valid.

Go also actually might not compile on the exact example you cited (translated to Go, of course) because that code contains an error if `v2` is never used after it is assigned.

In general, you cannot reason about the correctness of incomplete code snippets. This is not specific to Rust.


I think you've missed my point.

The ownership and borrowing model may provide many benefits, but those benefits come at the cost of complexity (as defined by Rich Hickey in Simple Made Easy). I have never found the owning/borrowing thing to be very hard. But it is _more complex_. There are _far_ more fundamental concepts baked into Rust then Go. And the interplay of those concepts creates complexity. It requires more knowledge and inherently complicates more things.

The parent that started this says "it requires so much thought". What that means is they have to hold a lot more concepts in their head. It's not that any one concept is hard. It's that holding all those concepts and the interplay in their head at once is hard(er). This is what people mean when they say "Rust needs to improve to attract more non-genius developers."

A huge portion of things you have to think about in the ownership/borrowing model simply doesn't exist with a garbage collected language. You just _don't_ have to think about a huge set of things at all.

Type inference is an example of this tradeoff that both Go and Rust agreed on. This adds complexity and decreases clarity in both languages. The upside is expressiveness and conciseness. We could always make you specify the type with a declaration (like C) and then the snippet you posted would need less external reasoning:

    int v2 = v + 1;
While you can't fully reason about any snippet of code, the more core language level concepts I have to address with any snippet the _harder_ and more complex it is for me to consider them.*

I'm _not_ trashing Rust here. I'm just stating that some of the good tradeoffs you made here leave the language inherently more complex and thus difficult to use. There are _upsides_ to that. You gain safety, performance, and "pressure to factor [your] system well". Simplicity is very far from everything.

When you're writing acme.org most of those tradeoffs are may not be worth the complexity. When you are writing a safety critical system they damn well are.

*PS on the subject of code snippets and compilation. As engineers, the primary audience of our code is other engineers. As such, I always find some programming language debates have real insight when addressed by my Strunk and White (no really). Consider the following two sentences:

The compiler must know the lvalue. It may then use it later on.

Too me this is the English language equivalent of type inference. We know "it" is "the compiler" based on previous statements. Using pronouns too often in a sentence makes it unclear. Using them too sparingly makes it tedious (and also often difficult to read).

A similar balance holds with concepts the reader must address in a given paragraph. This is the line we all dance when we write code. How many concepts must we hold at once to comprehend what is written?


Your invocation of Rich Hickey's excellent talk is very confusing to me. It seems like you're using "simple" the way that he used "easy." What is "complected" in Rust's type system? Just because there are "many" concepts, doesn't mean those concepts are entangled. I don't even agree there are that many - Rust lacks inheritance, reflection, and all of their attendant concepts.

In contrast, I find the way Go builds concurrency into the language 'complex' in Hickey's sense. Similarly the way that slices and maps are parametric, but other types are not. The implicitness in interfaces. The way capitalization determines privacy. Go seems like a very 'complex' but 'easy' language.

However, I'd even say I'm not convinced that I want my language to be simple. I want my system to be simple; its possible that by abstracting over an inherently complex domain, and by limiting my choices, a complex language can encourage me to create a simple system. I think ownership encourages the creation of simpler systems. I think a well factored system and a simple system are synonyms.


Binding and use of a variable is complected with its lifecycle in Rust. Which is further complected with type sytem via traits. To quote the docs:

> We’ve established that when ownership is transferred to another binding, you cannot use the original binding. However, there’s a trait that changes this behavior, and it’s called Copy. We haven’t discussed traits yet, but for now, you can think of them as an annotation to a particular type that adds extra behavior.

That's two additional concepts that interplay with variable binding. Two more _implicit_ concepts at that. In contrast maps being parametric doesn't affect other things (other than some syntax which is v0v). It is an additional concept to understand yes. But it's not intertwined with the other language features.

> However, I'd even say I'm not convinced that I want my language to be simple. I want my system to be simple; its possible that by abstracting over an inherently complex domain, and by limiting my choices, a complex language can encourage me to create a simple system. I think ownership encourages the creation of simpler systems. I think a well factored system and a simple system are synonyms.

I agree with you here (especially the later part). Again I do like Go, but I'm not fanatical on this subject. Languages can _certainly_ push you to best practices. Do Rust's? It's probably pretty domain specific.


Copy is essentially operator overloading (it is a simplified, restricted, moves-or-doesn't overloading of the assignment operator), which is always implemented through traits. I agree that operator overloading complects the behavior of built in syntax with the polymorphism system, but the alternative seems worse to me.

However, I don't think binding and use of a variable is 'complected' with its lifecycle. These are semantically significant concepts that I like Rust helping me manage, irrespective of their impact on machine memory.

Also, I should be clear that I do web development professionally and I think the best practices Rust encourages are absolutely applicable to that space, and general application development. That those practices are also high performance in Rust is almost incidental.


> A huge portion of things you have to think about in the ownership/borrowing model simply doesn't exist with a garbage collected language. You just _don't_ have to think about a huge set of things at all.

I'd like to introduce you to resource leaking and the Android activity/context lifecycle...

Just because you don't have to think about them doesn't mean that they aren't concepts that you should still be applying in GC'd languages. You'll just get bit by them when you least expect it or your load crosses some invisible threshold.

That said there's a valid point in that the large majority of software doesn't push the performance/memory threshold enough to make it a primary concern.


A portion is not all. GC doesn't solve all resource leaks. It does solve the problem of lifetimes.


It also doesn't solve all your lifetime issues. Do you know that the library you passed your object to didn't take a reference?

With GC'd languages you say "I don't care" to the lifecycle question which can(and will in any moderately complex software) come back to bite you.


That's not a lifetime problem. The object is still appropriately alive if referenced. The lifetime in the GC and free sense simply means that while an object reference exists it should be valid. See http://web.media.mit.edu/~lieber/Lieberary/GC/Realtime/Realt... for example.

GC doesn't guarantee you won't have resource leaks (of which memory is one). It guarantees that when you no longer reference an object it will be freed and that while you have a reference its valid. That's the lifetime problem. The Rust docs call it the same thing for that matter...


Maybe not strictly lifetime problem, but the unexpectedly extended lifetime can cause problem. The caller switches which object it is working on and now the live object in the library is disconnected. Or, if it is mutable, changes could be done by the library at a time which break caller expectations.

I've found that in GC'ed languages (like Python or JavaScript) not taking care of "lifetime", easily results in logic bugs. Better than crashes* - but still problematic.

* Maybe not always better either. Not crashing may lead to silent data corruption. And obvious problems (like crash) tend to be fixed faster than subtle ones, in practice.


Garbage collection does not actually solve the problem of lifetimes. At least not in the gc'd languages I am intimately familiar (c#/java). I cannot comment on whether or not Go solves the problem of lifetimes because I don't know the language well enough, but I feel confident that if it does solve the problem, it is with garbage collection in conjunction with another concept


Only if you restrict your definition of "lifetimes" to include memory. That said, memory isn't necessarily the only resource regulated by a lifetime, and that's why I prefer non-GCed languages for most things.


Is it creating an additional complexity cost or simply exposing an existing but often ignored cost?

It seems a bit like pointers, higher level languages will hide these details from you but you still have to know what they are and how they work, in addtion you have to know how the garbage collector works (that's a big complexity cost right there). In the same way that c forces you to be aware of pointers the borrow checker forces you to be aware of ownership.


FWIW, I've been programming in both Rust and Go for years and I can't really perceive a productivity difference between them.

I think it's uncontroversial to say that Rust has a tougher learning curve. But once you're past that, I think the jury is still out.


Exactly this. If you're a massively parallel web service, who cares about some GCs, and who cares about crashing like 0.01% of all transactions? Monitor it, crash it, fix it, deploy it, move on.

If you operated a nuclear reactor with this mentality, I'd be concerned. If you're operating acme.org with that mindset... godspeed?


If you're running amazon.com with that mindset, 0.01% of transactions is millions of dollars a day.


If you're running amazon.com, a project that takes 2 weeks longer to complete is hundreds of millions of dollars of lost sales.


Go has one GC benefit that a lot of other GC wielding languages do not; avoid using GC whenever sensible. Variables that are strictly local are almost always allocated on the stack, so a function return has free built in GC; a sizeable number of allocated variables never hit the heap. Compare that to say CPython where everything is allocated on the heap.


That's an unfair comparison. Python is notoriously bad with memory/speed in every flavor


I have yet to understand why people are always trying to compare Go and Python. Python isn't made for the same use cases as Go is. Can Go do the work Python can? To an extent, yes. Can Python do the things Go can do? To an extent, yes. Python will always be slower with the CPython runtime.

In short, I agree with you.


In the case of Google Go was developed in part to take over some of workloads previously done in Python; a language that is comparably as easy to develop in, with better tools and better performance. I suspect this is why the comparisons keep popping up.


>In the case of Google Go was developed in part to take over some of workloads previously done in Python; a language that is comparably as easy to develop in, with better tools and better performance.

Go definitely performs better, buy I think it'd disingenuous to claim that Go has better tooling.


Just by being a statically typed language, Go does have far better tooling and indeed many languages seem to copy its tooling concepts.

What Python does have is a far richer third party library base.


>What Python does have is a far richer third party library base.

I assumed this is what you were talking about. i.e. Ipython, Jupyter, pdb, etc.


numpy pandas scikit-learn beautiful soup libraries for geo, geometry, tags, parsers etc etc. Python has an enormous base of sophisticated third party libraries.


The thing is if you don't want to fight the borrow checker you can use RefCell and Arc/Rc. People are discouraged because then you lose a lot of architectural advantages that come with Rust but nothing is keeping you from writing unsafe{} and RefCell in the places where you know things are safe but can't formulate something that Rust understands.


I suspect that if you can't formulate it into something rust understands, you don't understand it well enough to know it's safe.


GC is not easier when you care about how and when memory is allocated/deallocated


I don't know, that is the kind of comment you used to hear from LISP programmers about how their program was really an elegant mathematical proof of its own correctness.

That's great until you try to do some real world things with it and run into endless impedance mismatches.

Rust isn't quite so far up its own butt, however the developer community doesn't seem to realize that there's a massive gap between the toy example programs in their documentation and the level of knowledge needed to build something worthwhile.


> the developer community doesn't seem to realize that there's a massive gap between the toy example programs in their documentation and the level of knowledge needed to build something worthwhile.

I don't think Servo and rustc itself are "toy example programs".


Are Servo and rustc in the Rust documentation? Literate programming of some sort? Maybe you're replying to a different criticism.

I've been through the Rust Documentation (doc.rust-lang.org) and Rust By Example (rustbyexample.com) several times now, and I have to jump on IRC to get answers to (what I think should be) some pretty simple questions.


If you think something important is missing from the docs, please open an issue. Steve Klabnik is very keen on making the documentation as useful as possible.


Steve seems like a genuinely helpful guy, but I'm not sure my most recent headache is a documentation problem so much as a deficiency in the std/core library. I was trying to write a generic function that called abs() on some of the arguments. Here's a simplified version:

    fn simplified_example<T>(a: T, b: T) -> T
        where T: Copy + PartialOrd
    {
        if a.abs() < b.abs() {
            // some irrelevant math goes here
        } else {
            // slightly different math goes here
        }
    }
I'd like to be able to call this generic function on anything that has a .abs() method. That includes i8, i16, i32, i64, f32, f64, and so on. Is that a shortcoming in documentation or an unfortunate consequence of Abs not being a trait? Several people on IRC recommended the rust-num/num crate, but that has several other deficiencies, and I don't think I should need another crate (unwanted dependency) to call .abs() on f32 or f64. With help, I came up with a workaround, but this was harder than I think it should be.

The grandparent poster said:

>> the developer community doesn't seem to realize that there's a massive gap between the toy example programs in their documentation and the level of knowledge needed to build something worthwhile.

I have to agree with him. The level of Rust-specific knowledge required to get the absolute value of a number/type in a generic function is pretty significant. The example above is just a small part of a library I'm working on and only one example.


We tried to get a trait hierarchy for numerical right four or five times; it's really hard. So yeah, you have to use num or make your own at the moment. It's true this is less than optimal; it's just not an easy problem. For just abs it should only be a few lines of code to make your own; three for the trait, and five times two for the impls.


> We tried to get a trait hierarchy for numerical right four or five times; it's really hard.

I'm clearly not an expert in Rust, and as such I realize my opinion isn't worth much, but it seems to me any attempt to build a numerical hierarchy will result in something fragile. Scheme has a well thought out minimal hierarchy, and even without all the complexity from all the fixed sizes I don't like theirs either. However, it's alright because they have dynamic typing as a release valve. C++ templates dodge the problem because they act more like duck typing, and I think I understand why Rust won't go that way.

Instead, I think you should abandon the goal of trying to create a hierarchy and just treat each small concept as a trait. Rather than creating Signed for things with a sign, and Float for things that are floating point and so on as in the num crate, create Abs as a small concept in its own. Then anything on which you can call .abs() implements the Abs trait. This includes things which aren't "signed", such as unsigned numbers and complex numbers - all of those have a useful (even if sometimes trivial) definition of .abs()

Trying to build a hierarchy reminds me of the stereotypical examples of bad object oriented design - I'm sure you know the cliches where Human derives from Ape, then Mammal, then Animal, etc... A hierarchy is like trying to build a directory tree, but numeric traits are more like labels on your gmail messages. Traits seem more like mixins or interfaces.

You'll end up with a lot of one or two method traits, but you'll also allow people to implement those traits for their own types and implement generic functions over those traits. The Float trait is a really good example of why categorization/hierarchy is unlikely to work well - That trait requires methods like .min_value() which don't make sense for complex numbers, but it is also the interface for .sqrt(), .exp(), and .ln() which have very good definitions for complex numbers. The Float trait in the num crate does not allow me to make generic functions over f32 and Complex<f64> if I want to call .exp().

> For just abs it should only be a few lines of code to make your own

Yeah, the IRC people helped me find a different workaround, but I agree that I'll just make my own trait instead. However, the .abs() methods already exist on all of the signed built in types and so I'll probably call mine .mag() on a Mag trait to avoid confusion (I'm not sure if there would be a collision/compilation error if I used the same names?). This also allows me to implement Mag for unsigned types which is useful for generic code (for examples L1 norms of vectors).

Btw, the docs for i64 have some cut-and-paste errors where they indicate i32 but almost certainly meant i64.


This is already the approach taken with the operator overloading methods with single-method traits like Add, Sub, and so on. Arguably things like .abs() and .exp() also logically belong in the same category and therefore should be implemented as small traits of approximately one method each.


Yeah, that might work, but then it leads to an explosion of traits...

The examples are cut and paste errors in a sense; as it says at the top of the page, the examples are going to often use the wrong number type, because all that code is generated by macros, so for now all the number docs have to use the same type. It's a bug that needs to be fixed, but it's not a trivial one.


> Yeah, that might work, but then it leads to an explosion of traits...

Yes, there are a lot of common math functions, but why is that bad? The alternatives are worse.

> The examples are cut and paste errors in a sense; as it says at the top of the page, [...]

I understand, sorry about that. I didn't see the comment at the top of the page, and I wasn't sure you knew.


Maybe, maybe not. Sounds like a good RFC :)

Also, no worries about the bug! I much prefer over-reporting than under-reporting when it comes to these things.


> Maybe, maybe not. Sounds like a good RFC :)

I'll think about it, but honestly, that sounds super unpleasant. I've seen the bike shedding that happens in those kinds of discussions, and I think it would be difficult to convince any decision makers that there is even a problem if they haven't stumbled upon it already. I don't think many of them do numerical computing, and the language and libraries already scratch their non-numerical itches.


That's fair; I will say that numeric computing is an area we care about. Still working on some of the precursor stuff to make it great, though.

There's a _lot_ of ways you can go in a general purpose programming language; I don't think it's something that "nobody has hit", they just use the workaround of making their own trait(s) and it hasn't been enough of a pain to try and spearhead a change.


This is a pretty neat idea -- I think I'll try this the next time I need to work on numeric code.


"Go is a blub language" was actually my hot take on this article.


You're treading on uncertain ground by insulting Lisp. :)


> A friend of mine put it into good words: Go and Rust are two big conclusions, to big follow-ups to the C and C++ world.

Go isn't a replacement for C; you can't use it for many forms of low-level systems programming, because it has a runtime. Go provides a high-performance replacement for things you might otherwise write in Python or similar, and in that capacity it works quite well.

Rust can do anything C can; its capabilities build upwards from bare metal, not downwards from higher-level languages. It intentionally doesn't have a runtime, and its approach to avoiding garbage collection (namely borrow checking) provides safe memory handling without a runtime at the expense of being a novel language feature without analogue in other languages. Much of the learning curve in Rust comes from that.


I think we like to say things like "C has no GC so it's good for kernels, Python has a GC so it's good for blah blah". In reality, we've definitely written plenty of "python level" (application level) software in C, and the same is happening in Rust.


> In reality, we've definitely written plenty of "python level" (application level) software in C, and the same is happening in Rust.

Oh, absolutely; you can certainly write higher-level code in Rust, and it makes for a far more comfortable experience than writing higher-level code in C. I didn't intend to suggest otherwise. And the ongoing futures work helps Rust address several additional areas that Go does well.

However, I don't think it makes sense to group Go and C together, syntax aside. Go cannot replace every use of C, because of its runtime and related expectations. (You can push it into some low-level environments if you make accommodations for its runtime, but you can do the same for languages like Python. You wouldn't necessarily want to do so in production, though.)


Once you get past the learning curve, Rust is a joy to work in, and you can switch your mind off to things that the compiler can help you with. The trouble is that it takes longer to grok than the four days most people give it. It's not really something you can bash into submission based on past experience with another language.


i continue to be astonished that D gets left out of these discussions. it's more capable than go and easier to use than rust, which would make it look like the sweet spot for a lot of people moved to compare the two. (that said, every time i've tried to use the language the tooling around it has been a miserable experience. that might have changed in the last couple of years.)


I've never used D, but I think it was written before the developer community was ready for a new language.

Now that developers are ready for new languages, D is already an "old, failed" language.

It reminds me of selling a house. If you put your house during a hot market and go into contract right away, but then your buyer can't come up with the money and it falls out of contract three weeks later, bringing your house back on the market it's now no longer "newly available!", and everyone thinks that, because it didn't sell in the first week, there must be something wrong with it.

My personal entirely uninformed opinion of D is exactly that: It's an older, "failed" language. It's not on the upswing in popularity. It didn't catch peoples' hearts and minds. And if only because of network effects, therefore it's not worth learning, simply because no one else is learning it, and so therefore its ecosystem isn't expanding exponentially.

Success of technologies is far less merit than one might expect. See: VHS vs. Beta.


You are right. D had some serious failures in the version 1 times, which tainted its image. However, the community is still growing. Slower than Rust, but moving in a positive direction.

During the last year the D foundation was founded and is spinning up operations. The 2016 D conference had a visitor record again with over 100 people. Downloads are growing over the years [0]. The package manager dub is now fully integrated. You can find success stories on the blog [1] and companies using it [2]. There are books.

[0] http://erdani.com/d/downloads.daily.png [1] https://dlang.org/blog/ [2] https://dlang.org/orgs-using-d.html


It was also commercial and closed source in it's early years wasn't it? I don't think there is a place for commercial compilers anymore.


D was always Open Source and free as in beer. It was not Free Software, which is a problem for inclusion in Debian main et al. You get the package from a third-party repo.

The reference compiler backend is still only free as in beer, so there seems to be no way to include "DMD" in Debian. However, LDC (reference frontend + LLVM backend) is practically in sync with DMD today.

The advantage of DMD is faster compilation. LDC gives you faster programs. So, for development I prefer DMD for the faster iteration.


Was it restriction free or did it have limitations on things like stack size like a lot of commercial compilers did?


No restrictions. There was nothing you could pay for in D.

The backend comes from the Digital Mars C++ compiler, which is/was commercial. That is probably the source of the confusion.


It will be a shame if that's the case.


Why is that? History with commercial compilers have shown them to create nothing but vendor lock-in.


More choice, I don't want an open source mono-culture.


Go is describes itself as "a dumb down language", because according to Rob Pike, "young engineers at Google are too stupid to understand C++ or Java" or something like that.It's true you can learn it in 3 days and be productive with it.

Learning D and Rust requires an effort Go proponents are not capable of.

Another problem is that both D and Rust try to replace C++ and C respectively. But C++ and C developers would never switch to anything else as they feel comfortable using the later, even despite all the gotcha of manual memory management.

Most people using Go today come from Python,Ruby,PHP,JS and co, not from C or C++ or Java. They want performances, without paying a price for "complexity". D is fine today, but it lacks a community behind it and a good ecosystem for web development.

If Rust had garbage collection, it would certainly be more popular but it's not going to replace C anyway. We are stuck with C and C++, they are not going away.

I understand why Rust is what it is, I also understand that most developers don't want to deal with explicit move semantics and lifetime management. Rust isn't going to seduce Ruby,Python,PHP or Javascript developers. the later only want better performances and think Go is good enough. They don't care about explicit memory management.

I'd like to see a version of Rust with garbage collection. The language itself is good.


> But C++ and C developers would never switch to anything else

There are many people who considered it and rejected, or even moved to other languages. C++ developers with existing large codebases may never switch to anything else, but that doesn't mean that the rest of the world is stuck. People are writing databases, browsers, operating systems and device drivers in rust right now and eventually some of those will become successful project defining state of the art.

> I'd like to see a version of Rust with garbage collection. The language itself is good.

There are myriads of languages with GC. There are very few modern ones without it.


D was also trying to bring a ruby/python-like experience to the C++ developer. can't say how well they've succeeded, but I feel it's definitely a strictly better language than C++ at this point. from my perspective its main weakness is not being able to generate a runtimeless library that I can call from other languages; without that it doesn't offer enough over ocaml for me to use it, but that would have been a killer feature. (rust does offer that, which is why it's on top of my to-learn list right now)


> I'd like to see a version of Rust with garbage collection. The language itself is good.

Why? What is the added value?


The added value is that you can implement things that need garbage collection. Like, say, a scripting language.

More importantly, it could be very useful to interact with code in garbage collected languages while having Rust model of the behavior of the garbage collector using its type system.


The syntax is nice, pattern matching, traits and generics are nice. The added value is a language with a better type system than Go in Go's space (web development). I don't want manual memory management or to have to care about regions, lifetimes or borrowing.


We have pattern matching, traits, generics, etc. in Scala (and OCaml?) already. The syntax isn't the same, but draws from the same lineage. If you took away the memory management, I think Rust would be just another ML descendent and I don't know what the motivating feature for the language would be.

I could be off base. I haven't programmed in Rust. I've just skimmed through the documentation and little code projects written in it every once and a while.


I share a similar sentiment. However, the language I'm referring to is Nim. It's just so damn expressive.


when i first saw d (years back), it merely seemed like an attempted c+++.

the syntax seemed extremely similar, as if they took c++ and just tweaked it a bit, rather than working at least somewhat from first principles.

seemed almost comically pointless at the time. no doubt it's evolved since then.


I'd like to point you here: https://news.ycombinator.com/item?id=13314770

TLDR: Rust did the hard work of convincing others to try it out and evangelize the language, and as a result, it has become popular and has grown a strong community.


Ah, this rings so true.

I've tried to get into Rust on/off and each time I'm left feeling like I needed a semester of instruction just to write a trivial program. Each attempt to go back to it, I would immediately hit a wall.

Go, on the other hand, felt quick to pick up. While I don't use it a lot, it's focus on a few composition patterns, and really sticking by that, made it easier for me to learn and come back to when I go back to my Python-centric existence.

Rust on the other hand, I haven't thought about nor touched, outside seeing an HN headline.


Grumpy isn't a way to move from python2 to go. It's a way to keep using python2 with the performance benefits of no GIL.


Isn't it transpiler ? I think they trying hard to move away from python2 to go, but they had massive amount of code written in python2.

I think your logic is not sound . If they wanted to use python2 (only with their own runtime which does not have GIL) instead of switching to go (eventually) they would write a runtime instead of transpiler ?

Please correct me, I am not 100% sure.


I've found Rust to be quite enjoyable, sometimes the borrow checker is annoying but you get used to it pretty quickly.

I only play around with it for personal projects but I would have thought that if I went from scratch and spent four days on it like the article stated that I'd be pretty productive.


This is one of the more reasoned differentiation I have seen between the two languages. Funnily enough, it's in both of their mission statements. It clearly shows too in the resulting languages.


"It's just a bloody fucking fuck" is reasoned?


> It's a pain to get working, it sucks to program in, it requires so much thought, it's just a bloody fucking fuck.

It's too bad that you feel that way but lots of people like programming in Rust and don't like programming in Go. I have never before had to pay so much -- in terms of type annoyances, packaging workflow and boilerplate -- for so little.


I wouldn't put Go as a follow up to C world. More like to Java one. But Rust is indeed a follow up to C/C++ world.


Well yes: touted as 'a better C' but I suspect they've succeeded at 'a better Java'. But it's more fair to say that Rust is a _retry_ at creating a C++-like language organized around concepts of safety and ML-like features ('like C and Haskell had a baby' as one person put it.) You can get _quite confused_ applying C++ concepts blindly in Rust ;)


I agree with all of that except that I don't see Rust as the safe follow-up of C but rather as the anti-thesis of C. Indeed knowing C will not help you. They're both imperative and that's pretty much it.


With the mix of affection and disdain one traditionally feels for a younger brother, i refer to Go as "systems PHP". It may not be a sophisticated language; it may not produce beautiful code; it may, arguably, even have inexcusable flaws. But it is astoundingly easy to learn to the point where someone can be productive with it.

This is a terrifically powerful, and very unusual, advantage for a language to have. Master programmers won't be choosing it as their tool of choice. It won't be used to attack the craft's hardest problems. But it allows a huge number of people to write programs they otherwise would not have been able to write, and, conversely, a huge number of programs to be written which otherwise would not have been written. There's something rather "quantity has a quality all its own" about it. As with PHP, this is something of a mixed blessing, but it certainly puts a dent in the world.


Master programmers won't be choosing it as their tool of choice. It won't be used to attack the craft's hardest problems.

I see almost the opposite, with many new distributed systems being written in Go.


I use a lot of this distributed systems tooling on a day-to-day basis: Docker, Kubernetes, Vault, Pachyderm, etc., etc. It's all brilliant and innovative software and I'm glad to have it.

But every time I need to dig down into the source code, I quickly realize why Docker falls over all the time with weird bugs, and why Vault's high-availability backend for etcd falls over once per week, and why Kubernetes is capable of inspiring such epic love-hate relationships.

There's a ton of code in Docker where nil can sneak in, and where the types of certain data structures are harder to determine than they should be. Sometimes nil represents "none", and sometimes the empty string does. There's a bit of multithreaded stuff in Vault's etcd backend (or was) that doesn't understand the corner cases inherent in concurrency.

I'm totally convinced that these new distributed tools are awesome and a boon to our industry. I just wish that somebody had a long conversation with a good type checker and thread safety checker at critical moments during the implementation process.

This may be a classic case of the "New Jersey approach": https://www.jwz.org/doc/worse-is-better.html Software that's sufficiently innovative and which works will be forgiven odd corner cases and bugs.


> But every time I need to dig down into the source code, I quickly realize why Docker falls over all the time with weird bugs, and why Vault's high-availability backend for etcd falls over once per week, and why Kubernetes is capable of inspiring such epic love-hate relationships.

Vault's documentation clearly states to not use ETCD as the HA backend.

Docker's problems stem from their bone-headed decision to start with a client/server architecture that they've been unwinding ever since. They should have started with small composable tools that integrated well with the existing infrastructure (init systems, file distribution, and security infrastructure). Instead they started with an architecture where everything goes over an HTTP socket through a daemon with high probability for contention, deadlock, and failure. None of that has anything to do with Go. They could have built that abomination in any language.

I can't speak for Kubernetes. I have not used it in anger yet.


I'm not sure what that has to do with things like race conditions that randomly result in "kill and delete" erroring and leaving the container in a invisble, dead state, with a totally useless error message. There are a lot of resources to keep track of in a system like docker and esoteric error conditions, and pretty poor tools to help you do it correctly in Go.


> Vault's documentation clearly states to not use ETCD as the HA backend.

Yes, when I reported bugs against the etcd HA backend they said they were going to add more prominent warnings to the docs, if I remember correctly. :-) I think the underlying bug comes from overly optimistic multithreading code using CSP, but I stared at the code for several hours and couldn't see how to make it unambiguously correct.

We now have our own in-house Redis "HA" backend that I wrote which appears to be rock solid in production, but which temporarily disables Vault during Redis restarts. So it's really "fake HA" but it works nicely for our use case and never requires manual intervention or embarrasses us during business hours. I refuse to set up and administer an entire Consul cluster for a single leader-election lock. Managing Consul has its own complications: https://www.consul.io/docs/guides/outage.html

> Instead they started with an architecture where everything goes over an HTTP socket through a daemon with high probability for contention, deadlock, and failure. None of that has anything to do with Go.

The Docker daemon's wire protocol suffers from a kind a of sloppy thinking about data types, in my opinion. Consider this line in a Rust Docker client: https://github.com/faradayio/boondock/blob/bf29afb0c78f8d5d6...

    pub Ports: Option<HashMap<String, Option<Vec<PortMapping>>>>,
Here, the `Option` types say, "This might be a JSON `null` value on the wire." So the `Ports` member might be `null`, or it might contain an empty hashmap, which in turns contains values that might be `null`, or might be `PortMapping` value. The corresponding Go code is extremely unclear (at least in most similar situations) as to which values can be `null` on the wire.

There's a lot of really foggy specifications going on in the network layer of the Docker server. It's not bad in the way that certain old Unix servers like `csvd` were bad, but I feel like Docker could be better if Go demanded more clarity about data types.

On the other hand, Docker is clearly a raging commercial success, and it actually works quite nicely (at least if you use Amazon's ECS to manage your clusters). So maybe I'm just being overly fastidious. Clearly all this new Go-based devops infrastructure is hugely successful, even if many of us get frustrated with it now and then.

Still, I really wish that more programming languages forced you to be clear about which values can be `null` and which can't. Hoare called it his "billion dollar mistake": http://lambda-the-ultimate.org/node/3186


I'm still unconvinced Docker's choice of using Go is the primary culprit for their problems. If Docker had started bottom up they would have been forced to design tighter contracts between the components from the beginning (regardless of language).

Ultimately it's the architectural decision to couple everything that allowed them to be loosey-goosey with internal-only interfaces. Why bother specifying them thoroughly if you can update all components simultaneously and have no dependencies outside of your control? I'm sure a tighter language would have helped in the small, but their problems are categorically big problems that I believe transcend smaller and more localized failings.

My experience using Consul for the Vault backend has been pretty good. Yes, you have to manually go in and clean up old nodes after a restart, but that isn't too bad. So far (knock on wood) we haven't run into a situation where a replacement node has refused to join the cluster or the cluster has out-right failed.

That said, I do wish they would put the effort to properly integrate with Etcd. We've gone through the motions of setting up and managing a highly-available Etcd cluster for quite some time now and are more confident running it than Consul. I'd rather standardize on Etcd myself, but I'm not inclined to swim upstream. :)


I have yet to see Go code among that class of projects that I would consider bad. I find Go to be a kind of equalizer -- the low ceiling prevents masters from moving upwards in syntactical/semantic complexity, and the high, uh, floor prevents junior developers from doing too much obviously bad. There are plenty of edge cases a newbie can cut themselves on (the addition of unsafe pointers is an antique, Victorian footgun that you'd not expect in a modern language), but overall, Go's forced simplicity normalizes code quality to a degree that I've not seen elsewhere.

Docker's faults do not come from Go, and the same goes for Kubernetes.


Well I do hope analogous tools appear in Rust and show design soundness of Rust. For now I remain sceptic that Rust is perfect language for large, complex, high-perf highly concurrent software.

Rust Roadmap says this much for production use:

"Production use matters for the obvious reason: it grows the set of stakeholders with potential to invest in the language and ecosystem. To deliver on that potential, Rust needs to be part of the backbone of some major products.

Production use measures our design success; it's the ultimate reality check. Rust takes a unique stance on a number of tradeoffs, which we believe to position it well for writing fast and reliable software. The real test of those beliefs is people using Rust to build large, production systems, on which they're betting time and money.

The kind of production use matters. For Rust to truly be a success, there should be clear-cut reasons people are employing it rather than another language. Rust needs to provide crisp, standout benefits to the organizations using it."

https://github.com/aturon/rfcs/blob/roadmap-2017/text/0000-r...


Is that because go is such a great choice, or because it was written by a famous guy for google?


You could read the top level article and find out?


Go does not deserve this comparison. It is a well-engineered and conceptionally sound language, quite the opposite of php: https://eev.ee/blog/2012/04/09/php-a-fractal-of-bad-design/


It isn't PHP-bad, but it isn't a great example of clean, orthogonal design. The only generic data structures being maps, arrays/slices, and channels, is not indicative of a "conceptionally sound" language.

Don't get me wrong, it a great, practical, engineering-oriented 90% solution kind of language; "conceptually sound" seems like computer language theory got involved, and it didn't.


I would never think "conceptually sound" has anything to do with PLT. Seems like an odd definition...


If you have not done it already, I recommend that you give Lua a try. There's a "Learn Lua in 15 minutes" thing that you might like.


To me, Lua feels like "Javascript done right". I only find the `end` syntax very annoying to write.


Exactly. I learned Javascript after Lua, and it was pretty similar, except a bunch of clearly worse design choices, and a pile of complexity mostly related to the web platform. (Why is this magical? Why aren't returns symmetrical to inputs? Why are the semicolons and other syntax so silly? Where are the cooperative coroutines? Why are the types and conversions so much more complicated without adding any additional value? Why all the extra ceremony around what is fundamentally the same prototype OOP system? Etc, etc.)


Yes, except for 1 based indexing.


Configure your editor to do it for you, just like most people do for closing braces.


Then you'll just have an annoying editor and an annoying language.


That seems rather elitist and dismissive. Why wouldn't master programmers use Go if it's the right tool for the job?


The craft's "hardest problems" are tackled with C or C++ (embedded and real time stuff) or with R, Python, Fortran, Matlab (scientific stuff). Some of the most used and therefore most scalable systems on the web use Java, PHP (and variations of PHP), C#.

None of those languages strike me as being both elegant and state of the art at the same time.

Meanwhile the "weapons for a civilized time" such as Lisp or Haskell haven't really gotten a major hold in either a Fortune 1000 company or the software giants on the West Coast.

It's like there's no correlation between "hacker points" for a programming language and the actual results delivered with that language! I'm being ironic, there isn't any. I'm open to someone proving me wrong though...


Well, you usually don't choose languages based on their technical merits, but rather on the ecosystems around them. These depend on popularity which you get by having traction, which you get by being quickly productive. This is why very tolerant or very familiar languages succeed. Being chosen as one of the blessed languages that are taught in universities is also, of course, priceless. A killer framework (Hello, Ruby) can also do wonders.

Not that this defends the very poor syntactical choices that Lisp and Haskell and their communities have done (minimal grammar is not a good thing! One-symbol names are horrible!), but a lack of popularity is not as damning as it sounds.


> Well, you usually don't choose languages based on their technical merits, but rather on the ecosystems around them

Ecosystem is part of technical merit, but I'd agree that in many cases (particularly non-software firms), languages aren't chosen by technical merit (including ecosystem), but for social reasons.


I can't bet for everything, but a lot of sophistication has been put into making Fortran, C, and Java very fast and efficient, for different but meaningful values of "efficient". They are very sharp tools, if not most elegant.


> Meanwhile the "weapons for a civilized time" such as Lisp or Haskell haven't really gotten a major hold in either a Fortune 1000 company or the software giants on the West Coast.

Last I checked, the list of publicly acknowledged production Haskell uses included several significant ones in Fortune 1000 firms, and various Lisps have seen production use at major firms, both inside and outside the software industry.


Sure, Haskell and Lisp (and APL and any other language you care to mention) is used somewhere, in some Fortune 1000 company, but that's a far cry from having gotten a major hold. Just because a group of futures traders in one HSBC office use Haskell to price a certain type of future you cannot really say the Haskell has a major hold at HSBC.


Upvoted because it absolutely is elitist and dismissive - it's a fair cop!

To pedantically respond to your question, note that what i said was that master programmers "won't be choosing it as their tool of choice", by which i mean that it won't be the language they dream about being able to use on their next project. Of course, if Go is the right tool for the job, a sensible and honest programmer will use it.


Well I was all ready to disagree with you and then you had to go and be reasonable.

That's a fair point.


The thing with Rust is it doesn't let you get anything wrong. It's both a strength and weakness. There's a million edge cases that you can just ignore in many modern languages.

A large part of the reason Javascript has taken off is it's ability to "get out of your way", however you pay for that in the long tail of issues and undefined things.

Many projects in software are trying to prove out a concept, and in those cases is makes sense to move fast and find out if the core thing you're doing works. However in problem domains that are well understood or have strict performance/memory requirements Rust really shines.

I think there's a strong argument that this is a subset of software(and probably not the majority) however I feel that unless you're taking a project to 100%(remember the last 10% takes 90% of your schedule) ESR's comparison isn't fair.


Whereas in C you can introduce subtle errors that might take forever to trigger, or silently corrupt things. Go still allows data races. Rust eliminates this.

So, I guess you can "ignore" stuff in other languages and feel like it's working, with less guarantees it is actually right. Most of the things you need to do in C, Rust just codifies and enforces.


Quick iteration on concepts is actually helped by not having to do extensive testing & maintenance work to keep your system from 500ing, which a static type system can help you do. The fact that dynamic languages don't care about your bugs is actually a hindrance.

What dynamic languages have as an advantage is that they allow you to more readily change your system as your understanding of the requirements change. This is where static type systems have traditionally been very weak - they create roadblocks for evolutionary change.

What would be great is a type system which catches your bugs but doesn't block you from evolving. I actually think Rust's use of type class polymorphism ("traits") goes a long way toward that goal, once you learn it.


Flow does this. Never used Typescript but I assume it's similar. Flow really shines though because the typing is totally optional and has almost no impact on the JS patterns that you want to use.


Do you have any specific examples of 'core, language-level things Rust needs to improve'? One of the most important goals of the 2017 roadmap is making the language more accessible, any actionable feedback is welcome.


Are you only asking the person you replied to? I have some topics I think indicate shortcomings in the language itself, but when I've brought these to the attention of core Rust developers in other threads of discussion the result hasn't been fruitful.


Which topics, and which threads of discussion?


I've had several discussions with you over the last couple years. I create throw away accounts on Hacker News, Stack Exchange, Reddit, Slashdot, Rust Users, and so on because I value my privacy. I don't want to tie my throw away accounts together, so I won't include links.

My experiences discussing anything Rust related with you has generally gone like this: I list several things I think are problems. You refute a small nit in only one of them and neglect the rest of the message. Giving the impression that you've successfully refuted everything I've said, you tag on the stick-your-tongue-out smiley :P

I've had other discussions with other core Rust developers. As should be expected, the conversations go differently for each.


Feel free to link them to remind me of the arguments that I'm neglecting. :P


You're interested in the links, not the arguments. I can't see how this is going to be fruitful.


I'm interested in the arguments that the links contain, but I'm starting to see why I apparently never bothered to fully answer you in the first place...


Your comment and others on Rust here sound a lot like people learning Haskell. There is a similar frustration with fighting the type system and weird concepts (Monads). I believe lazyness is the biggest problem with Haskell and Rust is eager, so Rust should be more successful than Haskell.

The Go comments sound a lot like Java in the early days. Easy language. Quick to get started. Easy to get lots of programmers. If desperate, just train them. Today Java is not so simple anymore, because the Pros wanted more features (eg Generics). I believe Rust will get Generics at some point. Typesafe containers are too useful.


I think you mean "I believe Go will get generics." Rust already has generics: https://doc.rust-lang.org/beta/book/generics.html.

I certainly hope the Go authors can find an implementation of generics with a set of tradeoffs that appeal to them. Typesafe containers certainly are extremely useful.


I like generics as well, but this comment (and embedded links) made me appreciate why Go doesn't have them yet:

  https://news.ycombinator.com/item?id=9622417
I agree, the value in generics/templates is in not cutting and pasting an algorithm or container to handle multiple types safely. A true macro system (and Rust's is pretty good one to model after) also solves this problem, and I wonder if Go would ever consider adding such a thing. It seems like a safer approach.


Did they consider the OCaml approach of "generic modules"? I would combine it with C++-style instantiation. By having it on the module level instead of functions/structs, you should be able to avoid the linker bloat. Are there OCaml users in the Go community?

Also, there is the hybrid method between C++ and Java, which C# implements: Instantiation for value types and type erasure for reference types.


> Did they consider the OCaml approach of "generic modules"?

Heh, I certainly can't speak for them, and I don't know what they've considered outside of that link. I also don't know C# or OCaml well enough to say if Go should/could adopt their strategies. However, Java's generics aren't very satisfying, and I think Go is wise to not jump in prematurely.

Hygienic macros (as in Scheme or Rust (Nim? Elixir? Julia?)) seem like a more mature concept that could solve most of the problem.


20 years journeyman here. If I could write my experience I would write what you did word by word about my feelings with Go and Rust. And I've tried many times - you nailed it.

As an aside, I keep seeing people don't "get" this, and it is painfully unclear to me - why?. Perhaps the pool of people with 15-20 years that are still doing programming is very small, so they make very tiny noise.


> Perhaps the pool of people with 15-20 years that are still doing programming is very small

I'm at 20 years of paid, professional coding, and 30+ if you count my Apple IIe/gs days and fooling around with C++ as an undergrad.

It took me about a week or two to make friends with the borrow checker, and mostly those were self-inflicted wounds from trying to write aggressive zero-copy code.

I think that Rust works very well for certain coding styles, but the learning curve is much higher for other coding styles. If you've been scarred by C++ and you're used to treating variables as mostly immutable, the learning curve is pretty reasonable. But if you work in a style with a lot of mutability and you've never wrested with "const char *" versus "std::string" in C++ (which both exist for a good reason in C++), there's more to learn. So I could legitimately see it go either way.

My handy litmus test: Do you utterly despise C++, or do you have a complicated love-hate relationship with it? If you fall in the latter group, you may fall in love with Rust on the first try. If you've never even used C++ enough to have an opinion, then it's possible you might not care about high-performance static languages and the prices they pay to go fast, and so Rust's trade-offs might not even be interesting to you.


One of my standard programming interview questions is, "what's the worst part about your favorite {language, framework, ORM, ...}?" I find that if you've used something to its fullest, you usually have a measured and reasonable response to that question[0]. If the candidate isn't as experienced as they claim to be, often the response will be along the lines of, "Well, nothing really."

[0] Unless that something is Rails templating because dang, that is some crazypants there.


Templating can't be the craziest thing in Rails. In fact, I can't remember the last time it gave me any trouble. Are you just talking about how ERB specifically works, or how templates are invoked/rendered? Or is there just some insane metaprogramming stuff in its implementation (there always is with Rails)?


Mostly it's about how the templating process itself works. I once had a strange bug involving how a variable was (not) bound in the view, and I believe I lost count between 20 or 30 steps from when the controller tried to render the view until someone or another called ERB.new.


Ha! I always tell people I have a complicated love-hate relationship with C++. It was my tool of choice for writing games for 20+ years (combined with Lua for scripting). I knew all the C++, and loved, yet hated, it.

Maybe I'll give Rust a go after all. No pun intended. ;)


Interesting,I absolutely despise C++, and I find rust to be incredibly nice (though admittedly, I'm a neophyte, I've barely had to use lifetimes, so I may be unaware of my own lack of knowledge).


The complicated lifetime stuff is pretty rare to run into unless you are doing some tricky things, like custom zero copy parsers or data structures or whatever.

The most important tricks I've learned are:

- Return types should usually be owned values, input types should often be references (or AsRefs). "Be explicit in what you return, and general in what you accept".

- If I'm going to be pointing into an array, its often easier to just pass around indexes instead.


Yeah I learned to return values and accept references very quickly, had to entirely refactor my learning project a day or so into it, but that was for the best.


What are your reasons for despising C++?


So to be fair, I haven't used a lot of C++, but what I have has been unlikeable.

I'm a pythonist most of the time, although I've done a not insignficant amount of work in C, but I like high level languages more, and C++ feels like the worst of all the worlds. It is complex and bloated (templates) and magical and all kinds of things. I think I summed it up one day by saying that, as a newbie to C++ my frustrations with the language were summed up by a 0 argument object constructor having no parenthesis if it was stack allocated, but heap allocation or arguments require parens, because the 0 arg constructor would conflict with a function prototype if it had parens.

There's so much bloat and cruft and oldness that the language has baked in inconsistencies and gotchas and so much stuff (which of the 12 different kinds of pointers should I use) that it is horrible to learn.

Rust on the other hand has complex semantics, but they are well defined, sensible, and logical. Sure I sometimes want to smack the borrow checker, but the reason why isn't "someone made it this way before I was born and now I'm stuck with the results".


I need to write C++ every now and then and I don't dislike the modern C++ itself, but the tooling is a bit rough to use.

Getting dependencies from the system is a big PITA and the build tools, as in cmake, seem to require as much knowledge as the language itself.

Having a C++17 with a tool like Cargo building static binaries and just the right versions of the libraries would make my life with the language much easier.


I've only used C++ since C++11, avoid polymorphism and raw pointers, and actually kind of like it.


>15 years programming here, from VB6 to JS including C(++/#), PHP, Java, Scala, many Lisps... I "get" what you mean, I just want to give my perspective:

When I tried Go I just didn't get what it had to offer. It felt like the wheel, reinvented. I was like "That's it?". I can see how that's appealing but, for me, Rust is a stepping stone on what we should expect from programming languages of the future.

A new language with a new paradigm is going to take longer to feel productive in, especially when the tooling and libraries aren't quite there yet. I can see how that's not good for someone who wants results now.

Rust's ecosystem is very much in its infancy. I expect 2017 to be a great year in terms of both language ergonomics (e.g. see Rust Language Server) as well as stabilization/standardization of very important things like how to do concurrent I/O. If your niche is distributed computing (e.g. I/O bound servers), yes, Rust is currently not going to satisfy you.

I think comparing Rust and Go is not fair for neither of them and, even if both are advertised as "systems languages" they are actually very different beasts filling their unique niches.


I'm excited for Rust to improve. I keep wanting to use it, but I don't have any projects that need to be faster than I can make them with Go (and frequently with less effort than the naive Rust impl). If Rust's ergonomics improve enough (tooling, std lib, and core language), I might be able to justify it. Fingers crossed!


> And Go _isn't_ awesome in a lot of ways (vendoring, I'm looking at you

It's worth noting here there is a large, active team from the open source community working on fixing vendoring and package management right now. I believe the target is the Go 1.9 timeframe (end of Q2ish this year).


I find that somewhat surprising. Are the dozen languages under your belt essentially variations of the same OO paradigms? I am mostly a javascript programmer, and a beginner at haskell, and I found Rust difficult not not that difficult.


This is the same experience I had with Go, I come from a Python background, I learned how to write webapps in Go and now am comfortable writing programs in Go. I don't find the lack of generics to be an issue.

This is an interesting thing I realized after I read the postmartem report of why "rethinkdb failed".

Users want something they can get and build apps with quickly, rather than something feature perfect four years after they want to build apps.

Go essentially answered the question, "I want to write a webapp, don't want to use Python or RoR, what do I use?". It is because of the easy to understand syntax and the powerful stdlib they were able to answer that question.

The language is amazing. I don't have any idea about Rust, so won't comment on that.


> I've attempted to learn Rust a few times, and it always feel like I'm moving uphill.

Isn't this also because it's actually quite a bit different from other languages? I think how difficult something is to learn is largely dependent on how similar it is to something you already know.

I can see how from a business standpoint, being able to start coding is great, but I also feel a bit sad when I see people not really giving a language a real chance, especially when a week or two may not be all that significant in the long run, whereas choosing a technology is potentially pretty significant long term.


I'm not seasoned at all; but trying Go a day made me, more than productive, creative.

I started to combine channels freeform and enjoy new ideas every minute, almost no cruft, almost no dead ends ... that's a rare thing.

They did circle the concurrency pretty neatly in a nice usable interface.


> to attract more non-genius developers

It's not like that, it's not hard to need geniuses, it's just too different for most people. And different takes time to learn. Which is definitely a design flaw for a language aiming for some adoption. I don't think they can improve it at this point, but they can provide a lot of value where other languages are weak.

More

Applications are open for YC Summer 2019

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact

Search: