A language that is touted as "difficult to learn" and "difficult to onboard" is by definition not built for maintaining. You could maybe make the argument that the guarantees provided by the language and compiler reduce the maintenance burden of software built in Rust, but I don't even really think that statement has been tested (via Rust being so young).
It is tiresome the frequency that legitimate language discussions for some reason turn into Rust vs. Go trash talking. It is almost certain that in 10 years, both will be used more than they are now by several x's. I think pretending you can predict what'll happen with either (and the industry) over that time is pretty silly. There will be legacy apps that people hate working with in all programming languages, and there's nothing that can save you.
It's my belief that whatever difficulty there is around onboarding people into Rust (setting aside the question of exactly how much of a problem that actually is or isn't) is "constant", where the difficulty of onboarding people into a legacy codebase is, shall we say, "polynomial" over time. Rust adds some constant overhead in exchange for reducing that polynomial growth, and my belief is that at some point the larger polynomials outpace it, ending up with more total difficulty for newcomers to a given legacy codebase. Of course time will tell.
This is an interesting way to look at it, but I actually think this perspective favors Go considering that there is less variety across Go projects than other ecosystems, including Rust. Of course no programming language can hold constant the business domain, but there are things like which “error handling framework”, “for loops vs iterators”, “sync vs async”, “prefer owned vs borrowed by default”, etc that apply to Rust and not to Go. And of course, Rust lets you build some gnarly abstractions which simply don’t exist in Go.
Go gives you less choice, and thus projects are more similar, and thus I suspect legacy Go code adds will be easier to maintain.
Of course this property may not endure once Go acquires generics.
It should be said that I’m a big fan of both languages, and I am especially enthusiastic about Rust in that it fits my compulsion to have code that is both very abstract and very performant.
Side note, Go gives you less tools - but not often less choice. Eg if you need to iterate and map, you still need to iterate and map. Go didn't change your application needs simply because it inhibited your ability to execute. Instead, you do what you need despite Go. Which often amounts to repeated functions, loops of loops, functions in loops to use defers, etcetc - you reinvent wheels. Go didn't reduce the application complexity, it just gave you less tools to solve it.
While some argue less tools is easier. I argue less tools often results in poor, messy abstractions. Those are traits that fight maintenance more than ease it, in my mind.
By the end of my Go tour, i was tired and wanted off the simplicity ride. The language was a burden, to me.
I disagree. First, making a language simple doesn’t make programs simpler, and often does the opposite.
I’m also not sure I buy the idea that Go is actually simple. It lacks a lot of features, but that’s not the same as being simple. I mean, it supports runtime reflection, which seems more complicated than features like traits or generics. And code using reflection is never simple.
Simplicity!
I believe there's a middle ground to be found where further abstractions add complexity rather than reduce it. Where that middle ground is, I don't know.
Anyone that had to work with Assembly in production was happy to have any kind of high level language to start with.
Not sure what's meant by this; Rust has a standardized Result type, with dedicated syntax for early-returning errors and type checks that make sure you handle them (or at least explicitly ignore them). Go, on the other hand, has a shaky error story and from what I've heard it's a constant problem due to enforcement-by-convention.
> prefer owned vs borrowed by default
Not really sure what this means either... the owner of a value generally falls out naturally from how you're trying to use it. It's not really an either/or option. It needs to live where it will be around long enough for everything that's done with it, and everything other than the owner should get a reference to it. Maybe you're talking about passing by reference vs implementing Copy? If so, that's mostly a performance concern.
> Of course this property may not endure once Go acquires generics
Yeah- one of the few things I like about Go is that it doesn't have generics (yet). I've seen generics make TypeScript codebases impenetrable, and I think there's a decent case to be made for not having them at all.
You may not like Go's convention (indeed, I don't love it although I think it works fine in practice), but the only error handling mechanism in Go is `if err != nil { ... }`. On the other hand Rust has many. This is not incompatible with your observation of "a standardized Result type". Here is a survey of the error handling landscape circa 2019: https://blog.yoshuawuyts.com/error-handling-survey/
> Not really sure what this means either... the owner of a value generally falls out naturally from how you're trying to use it. It's not really an either/or option
Or you have code which clones gratuitously out of convenience, often because some API unnecessarily asks for ownership of its arguments. Cloning is often just easier than battling the borrow checker, so in the real world with its deadlines, we tend to see a lot of clones because it's more convenient and performance is rarely a concern.
> I've seen generics make TypeScript codebases impenetrable, and I think there's a decent case to be made for not having them at all.
In fairness to TypeScript, I suspect much of this 'impenetrability' is because type annotations were added to existing JavaScript. I suspect the gratuitously complex types were already present in the original JavaScript, but the type annotations brought the pain to the surface. Similarly, I suspect TypeScript has a massive amount of developers who have only ever developed in JavaScript, many of whom are trying to shoehorn their bad JS habits into a statically typed language. You see this in other statically typed languages as well, but never on a scale that leaves a mark on the overall ecosystem.
That said, I think everyone should learn pre-generics Go if only to experience the value of an ecosystem that doesn't value egregious abstraction (again, I say this as someone who personally values egregious abstraction). And no, I don't think Java 1.0 counts (wherein people tried to shoehorn everything into implementation inheritance rather than generics).
By this definition, Go has many too. Go has lots of libraries to wrap error types, combine errors, preserve error data, etcetc.
Errors are one of the more similar things between Rust and Go, honestly.
All of this is helper functions built on the same identical core that your parent was talking about.
Not that that isn't its own kind of thing, but I think that the problems are different than "standard" vs "not standard"; this problem is only possible because there is a standard, and they all interoperate well with each other.
I apologize if my statement came off aggressive - I wasn't necessarily trying to say that you're shit talking specifically, just that it seems like lots of/most programming language discussions on HN devolve into some form of "Rust Good Go Bad" or "Go Good Rust Bad", which I think is usually just inflammatory.
Your prediction is 100% bias-based. There are equal (probably more) developers that would say the same thing you said, but with Go being the language that sticks and Rust being the language that's hated. The bias is the entire answer. An unbiased answer is "Who knows" and "Probably both will be loved and hated".
It actually sounds like your answer is more bias-based, because your intuition is more wrong here. There is data from the 2020 Stack Overflow survey[0] indicating a significant lead for devs who use Rust and continue to want to develop in it, vs. Go. 86.1% Rust vs. 62.3% Go.
[0]https://insights.stackoverflow.com/survey/2020#most-popular-...
That being said, there may be something about strict/complicated languages like scala and haskell that tends to polarize developer sentiment. Rust seems like it could fall into that same pattern based on the "up front" work that it demands.
Go and Rust don’t have similar numbers of developers. My intuition is that Go has an order of magnitude more. It’s like comparing product reviews on a shopping website - 4.7 based on 20 reviews vs 4.3 based based on 200 reviews. It’s hard to say which is better. You’d have to go deeper into each of those reviews to learn if the pros and cons apply to your use case.
Another important metric, as OP pointed out, is the age of the code base these devs are working on. Rust devs are probably working on code bases they started and Go devs are maintaining someone else’s code base.
That said, I agree with the current top comment on the thread - Rust code based may prove easier to maintain when we look back 10 years from now.
That's great to hear. Unfortunately, language popularity in enterprise/production environments isn't really based around how much a developer loves or hates something.
There are more Go programs/programmers now than Rust, was the basis of my post that you're objecting to, which can be verified if you scroll up on the link you posted.
I'm not so sure about that. (Or perhaps the definition itself needs scrutiny?) Most the languages I've seen get a reputation for being easy to learn have also subsequently earned a reputation for being a maintenance hassle: perl, ruby, python, php, javascript, etc.
Combine both these factors and you have a real amplified problem on your hands.
Uh, no. In Merriam Webster, that's meaning #2. Meaning #1 is
> to make much of : promote, talk up
> touted as the summer's blockbuster movie
> the college's much touted women's studies program
I'd argue, though, that the reality is that the word's meaning is in the process of shifting from being a synonym for "promote" to being a synonym for "make out to be"; that reflects much common usage, and dictionaries always lag behind a bit in cases like this. They have to - they're a descriptive artifact, not a prescriptive one.
1 : to make much of : PROMOTE, TALK UP
as opposed to
2 : to solicit, peddle, or persuade importunately
https://www.merriam-webster.com/dictionary/tout
Edit: uups! Didn't reload the page and see yesenadam's comment before posting. Sorry about that!
That's why so many threads end up being about Rust vs. Go. They are the new(ish) languages that appear most likely to be real factors in the industry for a very long time, so they bear more discussion.
I wasn't really around for it, but I'm sure there were lots of discussions about Java vs. C++ earlier in their lifespans, for this same reason.
Rewind to 1996. I’m at the Networld/Interop conference in Las Vegas. The steel skeleton of the Ballagio looms over desert mirages, and Java is all things to all people: a wide-open utopia to be populated by new and perfect code, a new world without concern for memory management, an abstraction from everything freeing us all from needing to know anything.
I disagree. The things that make for good maintenance correlate with both difficulty to learn and difficulty to onboard. (The converse is not true.) Things that are easily to learn tend to be easy to start with, but end up tacking on, via frameworks or coding standards, pieces that raise both difficulty to learn and to onboard as complex projects exist.
Specifically, maintenance requires thinking like the person or people who initially created the code. The more your thinking aligns with them, the easier it is to maintain. And that act of changing to think like them is learning and onboarding.
Big part of mastering is less about knowing the language and knowing its plumbing. Quirks, unique behavior, library, tooling, etc.
Dynamism is a constant source of pain. Because it makes tooling and thus exploring language harder.
In Rust there is little such dynamism. Actual problems understanding are macros and proc macros. And finding the right Trait.
Tooling around Rust is standarized. You have cargo, rustc and they bring in the rest.
While there is some chance of Rust going nuts and adding some bizzare thing like inheritance, HKT++, the chance that it will reach cognitive complexity of Scala or C++ is lesser.
While Rust has all the constructs that I'm used to from these other programming languages, I find Rust to be the most difficult one for me to learn in recent memory. The documentation is great, the book is great, the tooling with Cargo is great, but after getting through the first 15 chapters in the Book I struggled to write a program that had any sort of complexity. Sure I can add a dependency super easily to my project, but figuring out what errors everything in the library throws takes a lot of work. Sometimes I get confused about using panic!, expect, unwrap, or ? when I'm writing my own functions or interfacing with others. Swimming through the myriad of traits on different structs is confusing, and I sometimes get tripped up passing closures. And don't get me started on Macros, with great shame, they still have not clicked for me, in Lisp, or in Rust. The language just feels very different from what I'm used to, and I've never had such a hard time getting code I've written to compile correctly. I'd be totally lost without all the help from the subreddit and the book, and I appreciate everyone who has helped me.
I know this will get easier... and it has. But it will take longer for me to get comfortably with Rust then it has for other programmings languages in the same time period. This is not to say that it's a bad language. I remember one day after having debugged a Python issue that ended up being a stupid logic error on my part I exclaimed "I wish I knew a programming language that would save me from myself". I think... Or rather I hope Rust is that language in the long term for me
Coming from Haskell, Rust felt like a weird C-like home.
It's a weird marriage, but it makes for a language which make very predictable software imho
The reason it feels very different is... because it is very different. Then why does it feel off? Because, while it is very different, it doesn't look very different. Then why doesn't it look very different, when it is very different?
That's because, Rust developers spent conscious effort to make Rust look similar. So feeling something is off with Rust is the result of intentional design of Rust developers. In a sense this is dishonest and deceiving, but Rust without similarity engineering would be considerably less popular and hence less successful.
Rust's ambition was to change the world, not simply to discover a way to change the world and leaving actual changing to others. (Although changing the world requires discovery first.) So Rust chose to "lie".
As an example, Rust's assignment(=) looks similar, but works very different. In fact, since it is very different, it was originally written <-. It was changed to = specifically to make it look similar.
But <-> weirded people out, and people complained why Rust has so many special and strange operators. So Rust developers made it look similar to a library function. But it still is a primitive: it is impossible to implement swap yourself in safe code.
The result is that people use swap much less than they should, either because they are not aware of it, or because they think it is a specialized library function for special cases. This is significant and large cost. But it is still less than cost of looking weird. All because people won't even try languages with <->. Alas.
That's actually kinda disappointing, I use <- in my pseudocode.
(I don't know Rust though, so I can't comment on their choice for the language)
In Python, I'm likely to find a comparative mess of sparingly-documented documented third-party tools, dependency docs, and Stack Overflow posts.
Or, look at embedded, where C is considered a simple language. In C, it's not obvious how I'd start. Use something like Cube-MX or Keil? GCC? All the options are messy and tough to learn. In Rust, I clone a template repo, cargo-install 2 or 3 tools and targets, and `cargo run` to compile and flash.
I agree with this too.
This is because you know what Rust brings you compared to what you used in the past that cause you pain.
Unfortunately, some people that have difficulty learning Rust (or hear about it) focus solely on the learning process and not what they gain by learning it.
Learning a low-level language is inherently hard because there are more at stake, it is simply more complex. For people that work where dangling pointer, data-races, is never a problem, they are learning about the problem the same time they are learning about the solution (Rust's ownership concept), and I came from that place too.
Keil is a company that builds an IDE, compiler and debuging tools for microcontrollers. CubeMx is a configuration tool/code generator for STM microcontrollers. You comment makes no sense in regards to C language, unless you are saying that rust/cargo automagically does everything that Keil products and CubeMx do?
- `cargo install probe-run`
- `rustup add `thumbv8m.main-none-eabihf`
- `git clone some-template`
- `cargo run`
Low level drivers are usually handled using libraries of 2 general categories: Peripheral access crates (PAC) (mostly) auto-generated from SVD files; HALs that provide an abstraction over the PAC.
A month to get started sounds... intense, depending on the project. What drove (drives?) that? Let's say I want to do a simple project using STM32. Something simple that reads sensors, has a few buttons, and outputs to a display. I'd choose parts, put together a layout in a PCB design software, and order the boards, which arrive within 2 weeks. In the meanwhile, use a dev board as required. Find a driver for the sensor, screen etc if it exists. If not, write them using the datasheet. Use a HAL to take configure clocks and peripherals, write the control flow logic (Perhaps based on interrupts). For anything the HAL doesn't support, use the Reference Manual and the PAC to set the appropriate fields.
Setting up I2C might be something like this, using a HAL:
let mut scl = gpiob.new_pin(PinNum::P6, PinMode::Alt(AltFn::Af4));
scl.output_type(OutputType::OpenDrain, &mut gpiob.regs);
let mut sda = gpiob.new_pin(PinNum::P7, PinMode::Alt(AltFn::Af4));
sda.output_type(OutputType::OpenDrain, &mut gpiob.regs);
let i2c = I2c::new(dp.I2C1, I2cDevice::One, 100_000, &clock_cfg, &mut dp.RCC);Usually when developing a new platform for our products, I/we need to bring to life: SDRAM, LCD, touch controller, external flash for code and data, SD card + FAT32, USB, ETH, plus a bunch of other components (ADC, DAC, BT, other uC, latches,...) using the usual peripherals (UART, SPI, I2C, timers,...). Keil provides a good compiler and linker, fast JTAG debugger, RTOS and a bunch of middleware libraries (some are quality, some are lacking). While I dislike its IDE for writing code, it is a lot better than VS code for debugging purposes. It also supports flashing and erasing MCUs with internal and external memory, which sometimes requires special programming algorithms.
For us stability, tooling and good library support is the most important part of doing our job. Language follows from there. C and C++ have the advantage of the GNU/Linux ecosystem of open source to draw from and they map to the hardware almost perfectly. On higher level systems (Android, Linux) we use higher languages (with GC) which have even nicer tooling and better libraries for writing application code.
Rust is not more "difficult to learn" or "difficult to onboard" than C++. And C++ is certainly a language with plenty of legacy, enough that you could reasonably call it "brown". Go is a rather different language that's perhaps best compared with Java as its closest "brown" counterpart.
Saying Rust isn't more difficult than possibly the most difficult thing is not a good argument in favor of Rust not being difficult, no?
People dread maintaining legacy C++ systems. People dread maintaining legacy systems. Rust will be no different.
The 2018 edition had a few small breaking changes, the 2021 edition will have none. Personally I hope the 2024 edition makes some breaking changes to make IDE development easier.
So that someone familiar with c++11 or newer opening up a legacy code would maybe not have a hard time understanding it but they would go "wtf is this verbose shit" and be tempted to rewrite it. Vice versa an old schooler would need to relearn these new features when opening a new code base.
https://trends.google.com/trends/explore?date=all&geo=US&q=%...
I haven't seen anything that would indicate anything else than Go slowly declining much like Ruby has. In other words, it's still around and getting new updates, but losing mindshare.
https://trends.google.com/trends/explore?date=all&q=%2Fm%2F0...
That doesn't mean it will ever achieve popularity, and could also go away.
However, with the Go developers' insistence on changing the language as little as possible (not good or bad, it just is), it seems unlikely that Go will see a resurgence that brings it to multiple times usage than it has now.
https://trends.google.com/trends/explore?q=golang&date=all&g...
Referring to the language as golang is a secondary name, popularity driven by Google's own performance surfacing go (language) related results for the word go.
Go (programming language) has the same january drop as golang, clearer when both are viewed together:
https://trends.google.com/trends/explore?date=all&geo=US&q=g...
I don't think Go actually is substantially less popular in Febuary 2021 than November 2021 (January/December, maybe due to people/student vacation time), but the google trends anomaly is not a go vs golang naming distinction.
https://trends.google.com/trends/explore?date=all&geo=US&q=g...
Disagree. First, difficult to learn is not difficult to onboard. Second, unless you think Ocaml is not hard to learn, i think this is a great counterexample.
You contradict yourself.
I taught a university seminar that looked at rust and another emerging language a couple of years ago and only 2 students out of 30 said they would use rust again after the class. So the rest will never get a chance to answer the survey question about rust.
Strong disagreement here.
It could be difficult because it's built for maintaining.
People who are unable to understand Rust are the people you don't want anywhere near your C/C++/Rust project.
PL learnability alone is not significant as a factor for a PL to be chosen for a certain task, unless it is impossibly hard. Rust, looking at the growing community, is not a PL that is impossibly hard to learn.
A PL is chosen for a certain task for its features. The same as any other class of tool, "A is chosen for X because of A's feature".
The learning curve is simply the side-effect of the PL's minimum features need to be learnt for it to work. Rust being a low-level language + an extra layer of protection is inherently hard.
But let's see how it fares in 10 years. There have been successful projects built on Rust, firecracker, figma, discord, and many more, despite it being so young.
I think this presumes that you have to teach people the language. But most maintenance programming is done by hiring people already familiar with the language the existing codebase is written in, and then bringing them on to work on the existing project.
Rust isn’t hard to onboard for an individual project, in the same way that a macro-laden Lisp codebase is. Once you know Rust, you know Rust. It takes more effort to “know Rust” because you have to learn all the ecosystem stuff expected of someone who “knows Rust”; but from the perspective of an employer, you can just hire “someone who knows Rust”, and they’ll come with all that knowledge.
And, in fact, an experienced Rust dev may be able to start work on a novel-to-them existing Rust project, a lot faster than (for example) an experienced C dev would on a novel-to-them existing C project, as a large stdlib / conventional ecosystem of foundational libraries (as in the case of Rust) tends to translate well to experienced developers coming into a codebase and quickly recalling everything, because it’s a bunch of the same batteries/libraries they’ve already used in the past.
The difference between a design pattern and a language feature, is that you can recall language features, while you have to recognize design patterns (i.e. grok the code in order to identify what pattern(s) it’s trying to reify.) Languages with more formalisms (either built into the language, or in common use in the ecosystem) increase the amount of recall that new maintenance programmers can do (or rather, decrease the amount of recognizing they have to do to get up-to-speed), and thus make switching a maintenance programmer between codebases “cheaper.”
—————
I bring this up not from the perspective of a Rust programmer, but from the perspective of an Erlang programmer... who also doesn’t like Go very much (though I do write quite a lot of Go! As a maintenance programmer!)
IMHO Erlang and Go aren’t all that fundamentally different runtime-wise. Presuming you’re using them for the same thing — writing highly-concurrent, fault-tolerant network servers — the main difference between the experiences of coding in Go and in Erlang, comes down to the fact that Go provides you with a bunch of concurrency primitives that you have to put together as design patterns — and where maintenance programmers then have to recognize those patterns; while Erlang actually has universal concurrency formalisms that everyone takes advantage of, and so those formalisms can simply be recalled on new projects.
I advise anyone who thinks that “recognizing design patterns” is a cheap-and-easy thing to do, to tell me what’s going on in this file: https://github.com/ethereum/go-ethereum/blob/master/eth/down... . I’ve stared at this code on-and-off for two years, and I still don’t have a mental model for it. If this code was made out of cleanly-separated named/opaque formalisms, rather than fifty design-patterns tied into a knot, I can’t even imagine how much more productive I would have been working on it.
Does anyone know if there have been studies about how long it takes to learn different programming languages? I hypothesize that it would take a beginner programmer longer to learn Rust than C, but probably a similar amount of time as learning C++ (which I think is the closest “traditional” programming language to Rust).
edit I am amused at this downvote. I was asked a yes or no question, and responded with a yes. What could possibly make this response downvote-worthy?
Go was built explicitly to solve the real everyday problems of working in a large code base, largely driven directly by experience of several industry titans. It mostly remixes what has proven to work into a cohesive package. It's only as large as it absolutely needs to be (so new devs can learn it quickly), and has a strong focus on developer UX (gofmt, build speed, godoc).
Rust on the other hand is a far more ambitious language. It's trying to statically solve many types of problems which are typically solved through testing and experience. Using Rust productively requires much more Rust specific learning. With how Rust is changing and having multiple approaches to things as the language is defined, long living code bases are going to accrue different methodologies from whatever was popular when each component was written.
I think in 10 years a few experienced devs will do to Rust what Go did to C: Take the bits that obviously worked well, add a few ideas from other languages to soften some rough corners, and present it is a smaller, easy to use package. Though I also in general hope old languages are being replaced by new languages, as that must happen if as an industry we are getting better at building software. But also because I'm making a programming language and I have to believe I've found room for improvement for that to be a good idea.
Several quite opinionated industry titans who appeared to ignore most things they didn't themselves invent in AT&T for UNIX or in Plan 9, and based on their own preconceptions of "programming at scale" as a pet project (not based some formal Google-initiated research on its practical needs).
Algol and ADA were indeed created by "experience of several industry titans", in a process examining the field of applications and use cases for the languages.
Go was not really that, whatever the story says.
Based on their experience working at Google. It wasn't just "preconceptions". (True, it also wasn't an examination of the field of the form that led to Algol and Ada, but it wasn't just preconceptions, either.)
Yes, but that's their experience working at Google as 30+ years industry veterans and thought leaders (one would guess, mostly left to do their own thing, as prestige hires).
Not as regular developers doing everyday Google grunt-work...
"Creating the Go programming language featuring Rob Pike & Robert Griesemer"
https://changelog.com/gotime/100
There are other sources as well, this was just the one that quickest came to mind.
"Creating the Go programming language featuring Rob Pike & Robert Griesemer"
https://changelog.com/gotime/100
Had not been for him and Go would never gone beyond a prototype side project.
This claim keeps getting repeated with absolutely nothing to back it up, other than repeating what the golang authors claimed.
I work on a large golang code base, and it's quite horrid. The IDE experience is terrible. It's hard to navigate the code base, due to things like types unintentionally implementing interfaces because they happen to match on some of their function declarations. No proper visibility rules (only "public" and "package private"). interface{} everywhere because of no generics (I'm aware that's being worked on). Single character variable names are popular because the golang authors write code that way and it's seen as "smart". No reserved "this" or "self" keyword to declare methods means that every type will have its own single letter self variable which you have to look up every time. Error handling is terrible, and I've seen many insidious cases of mishandled errors. Null pointers are still a thing. The list goes on.
Edit: not to mention things like the language is against quick prototyping (heavy friction due to things like unnused imports or variables, and a very verbose language), as well as the lack of mature libraries such as JDBI or Dapper.
Python lacks a reserved keyword for this use, and I can count the ones not using “self” in instance methods and “cls” in class methods that I’ve encountered on the fingers of my left foot.
But, yeah, weak convention plus no keyword promotes needless chaos.
The above does not match my experience in maintaining large go codebases because of proper code reviews and expectation of writing maintainable code.
> interface{} everywhere because of no generics
It would be good to know the domain here. Looking at my own fair share of Go code in productive systems (probably around 100-200 KLOC, written over the last 6 years), interface{} is really really rare. It appears in some library functions which genuinely accept basically any type, in the same way as json.Marshal(), fmt.Printf() or sql.Stmt.Execute() in std.
Other than that, I don't think I've ever had a case where I used interface{} instead of defining a more specific interface (plus adapter types to implement that interface on foreign types if necessary).
The only big mess that I've come across in Go land is kubernetes/client-go. I can't wait for generics to replace this metastasizing heap of autogenerated code.
While that's technically true, it doesn't work out in practice because people look to the standard library as "the way" to write golang. It's basically an implied rule and I haven't seen anyone not folllow it so far.
> It would be good to know the domain here
Both infrastructure level work as well as CRUD type work.
> interface{} is really really rare
We have to use them everywhere for things like JSON parsing, config file handling, and pagerduty integration. Not to mention wrappers around SQL handling (e.g. batch inserts, pagination, etc.)
Generics will fix some things in the language, but not all. There are too many issues with it like the ones I already mentioned. It's still a very cludgy language to work with.
I assume you're not referring to things like json.Marshal(), where the function argument is legitimately of type interface{} since it really accepts any value.
Hence I'm going to assume that you're referring to situations where a value can have one of multiple types (e.g. a float value that sometimes gets provided as a string). It's true that people sometimes unmarshal into interface{} and then type-assert in those scenarios, but that use of interface{} is a code smell that should be flagged in review. The proper way is to unmarshal into a custom type with a json.Unmarshaler implementation that accepts all possible input formats (and vice versa for json.Marshaler if encoding is required). This is a real-world example from one of my codebases: https://github.com/sapcc/limes/blob/d833228e976ac1ddc4b95779...
Just as a start: variables names should always be clear by context. You only see the one letter ones if its literally the next line or similar use case. Also, bare interface{} should basically never be the case for a function signature.
None of the code you've described thus far would pass a proper review.
So have all languages, with wildly different philosophies about how to do that. We collectively just don't have any idea what we are doing. Clearly nobody has stumbled on a big win or one particular language would start dominating the industry due to the productivity bump.
1) They were either designed by one or two eccentrics, and thus contain a few great guiding principles and many questionable ideas, which invariably come back to bite large projects; or,
2) They were designed by a committee who tried to make the Final Universal Language that would solve every possible problem, resulting in a kitchen-sink mess of a language with a 400-page specification that no one reads.
Most mainstream languages made some attempt to serve software-at-scale; for example, OO used to be the obvious thing that would make software more maintainable, so most languages support it at least in part. But if you look at a language like C++, it's obvious that many of its features were added simply because they were cool or increased expressiveness (e.g. operator overloading) without regard for long-term maintainability. Even things as mundane as "implicit 'this'" are clearly harmful to readability, with specious benefit.
AFAIK, Go is the only language that really takes engineering seriously: https://talks.golang.org/2012/splash.article If there are similar articles for other mainstream languages, I'm certainly curious to read them and see how they hold up today!
Ada is the consummate engineer's programming language. And with Spark, even more so. Unfortunately, the compilers were super expensive at a critical stage in its evolution. And the DoD thing.
And I've never used Rust professionally (unlike Go), but it has all the applicable points in the article you list (obviously no garbage collection). But in Rust, these features are often implemented in a more robust manner than in Go (eg, like Go, Rust avoids exceptions and uses error values, but instead of returning a special-cased interface, it returns a regular sum type, which allows for proper type checking of the error handling, and which is also composable since it's also a monad). And unlike Go, Rust got package management down from the start. Go's package management has gotten better, but it's still not as good as Rust's through Cargo (to be fair, Cargo is best of class).
I think it would be a difficult argument to suggest that those other people do not have similar engineering chops, or haven't been around a long time.
But if you are right we should see Go winning in the marketplace more and more in the future in a way unprecedented for programming languages.
Ada is another; it was explicitly designed for large-scale engineering, but hasn't seen nearly as much success as Go. I'd like to learn more about why it failed. Perhaps it was the language itself, or maybe it was merely a timing issue, or the fact that it was backed by the government. This comment has some good info: https://news.ycombinator.com/item?id=7825008
The language was a mandate from US DOD which rubbed a lot of people the wrong way. It largely sat in the same space as Fortran and C, C was free (or much less expensive) and more readily available even when it did cost.
Cosmetic: People strongly dislike the verbosity of Ada. Some parts are reasonable to consider changing (but really are just cosmetic) like begin/end versus {/}. Other verbose portions are hard to change without fundamentally altering the language. You want to print integers and strings and you don't want to have to use Integer'Image(X) all over the place?
with Text_IO;
use Text_IO;
with Ada.Integer_Text_IO;
use Ada.Integer_Text_IO;
Now Put and Put_Line can be used with both strings and integers. Oh, you want to print more types? More withing for you. I don't mind it, you don't need to print in every package, but it is very cumbersome.If you want to write a quick CLI tool, Ada is not your language (though I've done it), go for Perl, Python, Ruby, sh/bash, or even Go. C and C++ can be easier for that purpose. But once you start scaling it's a very good language.
The only way to get Ada in the mainstream now is to make it much cheaper and greatly improve the FOSS compilers and tooling, and have SPARK/Ada support an increasingly large subset including approaching feature parity with respect to memory safety as Rust. That's being worked on, but it's a non-trivial problem.
The shortcomings listed in that talk are mainly against C and C++. Other serious languages and platforms like Java, C#, and also Rust have solved these issues.
I think what you refer to as differing philosophies are in fact differing goals, and the goal of Golang is very different from pretty much all other modern languages: simplicity.
I think boiling everything down to 'they are all trying to do it better' misses the fact that the 'it' they are trying to do better is very different in some cases.
This has to be quantified. Simplicity in the language, perhaps. It definitely does not translate into simplicity of resulting programs, and the ease of being able to reason about them, or write them.
The golang authors' weird obsession with not having any methods on strings for example, makes it horrendous to work with them (e.g. can't chain someString.trim().toUpper().endsWith("...")).
Don't get me started on the supposed fast compile times. Parsing is fast (but so almost any other language that is not C++ or Scala), but linking is horrendously slow. Modifying and re-running individual unit tests takes seconds at a time due to relinking. In Java, it's in a blink of an eye because there is no linking.
Java has no linking, but you have to go through the JVM. Golang produces native binaries. Not really a fair comparison. That being said, my brain is normally a way larger bottle neck than linking time for unit tests.
I'll cede the gripes with enums and error handling (although I hated the way Java exceptions always ended up way too overloaded way more), but I find the typing a really good balance between strong and weak. It's strong enough that you have to be doing something really weird to mess up, but weak enough to be flexible.
Young at heart :)
This is what happened with Python. It provided a distinct productivity advantage over C & C++, and now no one writes web apps in C++ anymore. Granted, it's not just Python anymore, but newer languages learned from Python's success.
Python's popularity for web app development came way later (django was released in 2005)
Notably, we've had garbage collection for nearly as long as we've had programming languages - so this has been going on much longer than the examples you give would suggest.
The lack of abstractions often force you to read a lot of code you don't care about.
The language is so simple that reading complex code is actually hard.
If you start a new project in typescript, or even C#, you'll get almost all of the things that made Ruby great in 2006.
When I introduce a new developer to Ruby, all I've got to argue in favour of it is that it's a beautifully designed and powerfully expressive language with a library ecosystem chock full of de-facto standardised gems. Relatively vague things, and any newbie can hit me back with its downsides: mediocre performance, no type definitions, and no evented I/O baked into the language.
I do dread the day that I can't start a new project in Ruby, simply because my hiring pool would turn into a mire of people dreading to work on it.
Ruby 3 addresses each of those issues.
https://www.ruby-lang.org/en/news/2020/12/25/ruby-3-0-0-rele...
require 'async'
require 'net/http'
require 'uri'
["ruby", "rails", "async"].each.parallel do |topic|
Net::HTTP.get(URI "https://www.google.com/search?q=#{topic}")
end
I can't tell someone Ruby is beautiful, and then in the next sentence explain how you need to type Async twice just to get something to run asynchronously.Same of course for Ruby types, we're just not yet on the point for a truly new way of writing Ruby for Ruby 3. Maybe we'll finally get something like this could be in Ruby 4, but I fear the spectre of backwards incompatibility is going to stand in the way of something that's both truly Ruby and truly in the 2020's.
Then, if we're talking rails (which is a significant part of the ruby projects out there), there is way too much magic going on.
Magic you have to manually un-peel when something goes wrong.
The way Ruby looks like, it was done in Smalltalk first, with performance, good graphical tooling and developer experience.
If you mean Rails, Zope, AOLServer and Vignette did it first.
I can gladly educate you on how Rails did not improve on the stacks already available, rather bring the concepts to a younger generation that never learned about them in first place, starting with ActiveRecord.
I'll gladly receive that education, I have a hard time believing any of those frameworks are as sophisticated today as Rails 2.0 was back in the day.
I could tell how they looked like from startup rooms in little offices spaces in Lisbon back in the early days of .com wave in late 90's, how AOLServer influenced the creation of Intervento's Safelayer framework, or how it got us to be approached by MSFT Portugal for adapting the same ideas to then still alpha version of .NET, however I am the right person to fully tell the story, better let that to the key persons, in case they feel like jumping in.
In any case, here is some education material for yourself,
https://zope.readthedocs.io/en/latest/zopebook/index.html
https://docs.huihoo.com/aolserver/intro/features.html
As an example, one celebrated feature of Go is that "all Go programs looks familiar," i.e. it's easier to get oriented in an unfamiliar Go codebase than just about any other mainstream language. Partly this is due to `gofmt`, but also the fact that the language itself is relatively spartan, with few dark corners. It's an "unsophisticated" language, which obviously cuts both ways.
Rust, by comparison, is a highly complex language. It allows for very powerful abstractions -- as well as very bespoke and "clever" constructs. I think Rust will develop a solid reputation for correctness, much like Haskell; Rust programs will run for many years without bugs or performance issues. But, again like Haskell, Rust will also have a reputation for allowing clever programmers to write too-clever code. That's why Go will prove to be the more maintainable language: because it intentionally limits expressiveness (a highly unpopular decision, unsurprisingly) and thereby limits the amount of "damage" any given programmer can do.
Other choices such as structs being initialized with “zero values” in their fields (instead of having a compile error when you forget a field), and the complicated sometimes equivalence of zero values and null create a lot of footguns when refactoring.
Go’s strength is a batteries included stdlib and its looseness can make prototyping faster. But it feels very much like an old school scripting language like php or js, where you have to use countless trivial unit tests to have any confidence about what your code is doing.
Ada, Eiffel, Java, Pascal and derivatives, C#, Rust. Probably many others but I'd argue outside of tooling (which Go is particularly good at and has done an incredible job of standardizing, Rust being a peer in that regard with cargo) these languages are as good or better than Go for software engineering.
I'm sure that the Rust team wants Rust to be a good language for large-scale engineering, but it's not their north star; "fearless concurrency" is probably the closest. For example, Rust is willing to sacrifice simple syntax and fast compile times to achieve more safety; Go is not.
I don't know enough about the other languages to speak confidently about them, but if you can point me to any source materials (i.e. articles or talks by the language creators) that discuss large-scale engineering, I'd be interested to read them!
> Quickly emerging as the language of choice for developers of quality software, this guide explains how the Eiffel language combines rigorous software engineering principles with advanced object-oriented techniques. It reveals the simplicity, consistency, and blend of various powerful mechanisms in Eiffel, including: an object-oriented structure based on classes ; multiple and repeated inheritance information hiding assertions to guarantee, document, and test the correctness of software components; strong typing backed by support for generic classes; dynamic binding a highly dynamic run-time model supporting garbage collection renaming and redefinition facilities . . . disciplined exception handling and support for persistent objects. As both an implementation language and a high-level notation for analysis and design, Eiffel emphasizes the industrial production of reusable software components. Written for software designers, analysts, and programmers.
That's just a more recent slogan to succinctly express what their real north star means. Their real north star is "safety and correctness", and like all languages, they push to make the most important thing to them easy and approachable.
Here's the thing, I'm not sure as a project saying your focus is large scale engineering and pushing towards that actually gets you closer to the desired result in the end than pushing for safety and correctness instead. I could fully see a language that pushes for safety and correctness eventually being a much better and easier to use choice when evaluating with respect to large scale engineering, because those are extremely important to large scale engineering.
It's good to have most of the core values in common, but a language's guiding principle manifests in many small ways that can be very compelling in aggregate. Go's weird capital-letter namespace visibility rule is a great example of this: it has nothing to do with safety or correctness, but it's enormously helpful for getting your bearings in unfamiliar code, because it localizes an important piece of information, saving you a context-switch. Sure, another language might hit upon this feature incidentally (Rust has conventions around capitalization, and IIRC Haskell enforces it to some degree), but how many languages will chance upon all of the little things that make for a great engineering language?
Overall I like Go, but for software engineering in the large Ada, in contrast to Go, has:
Explicit separation of specification and implementation of packages and types making it easier to shard the work out to numerous individuals and teams.
What a package exports is explicit, not the result of case conventions.
package Foo is
procedure Bar(N : in Integer); -- exported
private
procedure Baz(N : out Integer); -- unexported
end Foo;
package body Foo is
-- implementations of the above
end Foo;
Generics from day 1, which enabled a set of collection packages similar to C++'s STL in Ada 2005 among other things.Tasks, similar to Go's goroutines, but have a lot of additional guarantees and behaviors that have to be made explicit in Go.
Protected objects and protected types which permit you to write code without explicitly acquiring/releases mutexes and using condition variables.
Design-by-contract with SPARK/Ada becoming a proper subset of Ada in Ada 2012.
Not entirely. Some amount of maintainability is fixing mistakes that result in less correct or safe programs. By reducing occurrences of these classes of problems later, you're reducing the maintainability burden.
It's the same thing that makes a language with a type system more maintainable in the long run. Eliminating classes of errors pays dividends later. The decision to make is whether the extra effort at the time of creation is worth that reduction. Depending on the type of software, it's expected lifetime, and how many people are expected to use it, that may differ. For large scale engineering, I imagine the extra effort required up front is almost always worth it.
Here's the original elevator pitch from 2010:
https://github.com/rust-lang/rust/blob/d6b7c96c3eb29b9244ece...
Note the text about "programming in the large".
Design-by-contract. The contracts aren't just comments, they aren't haphazardly enforced by adding asserts to the code. They're explicit parts of the language which allows for a lot of interesting consequences when utilized. Testing can be semi-automated directly from the code, some elements of the program design can be proved to be satisfied statically without the need for compilation and execution. The ability to encode your specification into the language is incredibly useful.
Command-query separation. If methods return a value (queries) they should be referentially transparent, having no side effects. This is a principle so not an explicit language feature, but it can be enforced using DbC.
There's nothing to back up this claim. Using golang in practice shows otherwise. The mess resulting in large code bases is much more difficult to deal with compared to Java for instance.
Baking ownership into the type system makes API design and refactoring hard, while having GC makes memory management entirely orthogonal and easy to change. Go is already over 10 years old and designed by people who have worked on software for 30, sometimes 40, years. Go changes slowly and Rust is still evolving.
The opposite, it makes refactoring really easy, because it's so hard to screw up mechanical changes.
The memory management aspect of refactoring is slightly more tedius in rust (compared to go), because you have to update lifetime annotations sometimes. This is a mechanical process and not hard. The trade off is that the existence of lifetimes and ownerships means that many of the rest of refactoring tasks (getting locks right, not introducing data races, making sure you're not doing something dumb like accessing a freed fd, etc) are much simpler.
Rust is also better than go at refactoring because go's ducktyping (and reflection) means that there is frequently action at a distance that is hard to track down. Changing the name of a field of a struct can change behavior in another file that never mentions the field by name.
Go is in the middle of adding generics to the language, I don't think Rust has made a change of that magnitude since it hit 1.0...
It is much more complicated. You sometimes have to implement clone, pin/project, box etc to satisfy new trait bounds for example. Also, build times can make refactoring and iterating really painful.
I like and use both languages; I think they are complimentary, and I think things need to be weighted more honestly than what I see in this discussion.
My honest opinion is that it is 2021 and it's about time we let computers manage memory already.
Rust can have their ownership for race conditions if they want, but I am so tired of being gaslighted about how important it is that I be hounded to track every solitary byte of memory when I am busy thinking about more important things.
On the contrary, ownership tracking is important for general code correctness, not just memory management. It's practically impossible to audit and assess correctness of a program with pervasive confusion in the overall patterns of what code owns what data. Memory safety is also important of course, but it's mere table stakes.
Is the antidote to those inefficiencies an ownership model that forces you to reason about mutability? I don't know. I kind of think no, primarily that it is a performance consideration that infects the API and spreads everywhere; it's a distraction. Is it instead to have all immutable data structures and an insanely smart compiler that replaces copying with in-place mutation, so that pure functional languages compete with highly tuned, pervasively mutable code? I kind of also think, no. And primarily that's because performance cliffs get worse and harder to predict, the smarter your compiler is.
The mutability/ownership question is confounded with allocation and deallocation. The latter really should never be on any programmer's mind, IMHO. In Rust, it seems there isn't much support for decoupling the two, e.g. by having an automatic garbage collector. That's also an unfortunate reality forced on language implementers by the fact that LLVM has steadfastly refused to support stackmaps as a first class concept for more than 15 years. Many, many projects have died because of LLVM's stackmap support being lacking or broken. That's unfortunate because precise stackmaps are a key enabler for many GC techniques, and without them, you end up with some form of conservatism that make certain optimizations impossible, and forces a particular design for nurseries.
> I kind of think no, primarily that it is a performance consideration that infects the API and spreads everywhere; it's a distraction.
is something I fundamentally disagree with; unfortunately, I don't really have the time at this moment to even get it into the level that you have here, but it's a good conversation to have, and a needed level of details to even have a good conversation.
This is completely irrelevant because stop the world pauses are global and affect your entire application no matter how careful you write your code. Every library you use has to be carefully written, every single line of code you write has to be carefully written regardless of whether it is time critical or not, this is a complete dead weight for the entire application.
With isolated GC heaps your time critical code will be isolated from non critical code. In practice the easiest way to do this is to just launch a separate application. This hurts a lot with the JVM because it wants to own your entire server (mostly RAM) for some inexplicable reason and you might as well write that part in C++ if you do inter process communication anyway.
The real use case for GC is managing data where there's no well-defined "ownership" pattern, such as when dealing with general graphs (e.g. in a symbolic computing or GOFAI application). That's a remarkably niche domain.
Not having to worry about that is very nice. Using less memory, I agree it's not terribly important but it is a nice bonus - which comes in handy when working on embedded, videogames or in constrained conditions (like when you can put a lower limit to your containers and run more containers on the same machine).
Garbage collection lets you avoid memory leaks, double-free, use-after-free, and that's it. In Go, it's up to you to correctly handle every other type of resource correctly, as the compiler won't offer you any help.
An ownership system like Rust's is a general-purpose tool that handles all kinds of resource management. File descriptors, database transactions, session types, locks, etc.
People praise Go's channels, but Go channels are way more complicated to use correctly, and part of that is due to ambient ownership (the other part is due to bad error handling). You need to manually keep separate track of how many outstanding writers there are are, and then explicitly close the channel. In Rust, the channel is closed when all senders or all receivers are dropped. I don't have to do anything, and it's always as correct as what I'd have to implement and maintain myself in Go.
Another great example is how Rust's mutex can own the guarded value. In most languages, a mutex is a separate object, and it's up to you to be careful to make sure you never access it without acquiring the mutex first, to always release after the last use of the guarded value, and to never keep a reference to the guarded value after releasing the mutex.
With Rust, the ownership system is used to handle and verify all of that, so I do less work, have less to think about it, and can't make those mistakes. The Mutex owns the value. Mutex::lock() and ::try_lock() return a guard that you dereference to access the protected value. The mutex is released when the guard is dropped. The guard borrows from the mutex, so any attempt to keep it around longer than you have access to the mutex is flagged by the compiler.
My honest opinion is that it is 2021 and it's about time we let computers manage all of our resources, instead of only memory.
Addressing the last part of your comment, that doesn't sound like my experiences writing Rust at all. Regarding memory management and ownership, the majority of the code I write in Rust, I just type the obvious thing and it just works. The majority of the remainder, the compiler points out that I've done something dumb, and it's a trivial fix. The rest of the time, I'm trying to do something genuinely complicated, and the compiler helps me figure out and verify the work in pieces, rather than trying to cram the whole thing in my head at once, and then convince myself that it's correct. When I'm finished, I feel confident that the compiler will catch errors introduced by future maintenance and refactoring, rather than having to cram it all back into my brain at once every time I touch the code.
To me, in both Rust and Go, the trivial cases are trivial. For more-interesting cases, Rust lets me just do the thing and focus on thinking about my problem at a higher level while trusting that the compiler will handle the details. For more-interesting cases with Go, I have to keep track of a bunch of little details and make sure I explicitly clean up every little resource I ever interact with.
I really like the discussion on resource types vs data types in this essay on higher-level rust-inspired languages: https://without.boats/blog/revisiting-a-smaller-rust/
What Go defer lacks, is the ability like e.g. .NET with IDispose, where I can make of Roslyn Analyzers to break builds when I forget to call using on such resources.
Rust is a good option for kernel code and drivers, beyond that there is no point in throwing away the productivity of having a GC around.
The fact that you haven't mentioned these low hanging fruit makes me feel like you haven't thought your argument through. You're advocating for a half baked solution even though there is a perfect solution that almost nobody tapped into that would make the GC superior almost everywhere. That's why you are wrong and why nobody should listen to you and people should listen to the creators of Erlang and Ponylang instead.
In the field where I am working (realtime audio), that is just not possible. I don't want to have a garbage collector pause in the audio thread. In fact, I can't do any dynamic memory allocations at all (without a special realtime memory allocator). Other fields, like medical devices, flight control, embedded systems, etc. have even tighter constraints.
Manual memory management is not only about performance, but also about predictability. This kind of low level control is one of the main reasons why people use C/C++ in the first place.
I don’t code in go so this looked surprising to me. TypeScript has a similar feature regarding types, and it will tell you if you change a field in a record, and that record is actually used in a place where another record with the same fields is asked for.
Kind of surprised go has this problem.
Apart from serialization I've also seen this with ORMs, and some of internal code for metrics. I imagine there are other cases too... but while I currently work in go I'm not really that much of an expert in the language.
But how is it any better than Java or C#?
And Rust is a low level language, so it has to get away with rare cases where a GC is not applicable. And being low level of course imposes the memory layout of the program on APIs as well, but that is a known trade off when one goes low level.
Rust is unbeatable for services you want to deploy, stabilize, and forget about forever.
Go is pretty darn good for building out complicated/messy services in an iterative process.
Erlang.
C or C++ could have a GC as well (in fact, they do - you can use Boehm for any C or C++ program), but there are approximately 0 programs that do.
I wrote some Go code for talking LDAPs and doing some CRUD Ops. Do I love this code? Nope. Do I love the product that I built? Very Much.
I think love of language is somehow very much a Rust thing at least compared to Go.
> Rust on the other hand is optimized for maintaining
This seems to be a tall claim. So often in Rust some or other library, web framework goes defunct or abandoned. And they are not exactly in finished state so no maintenance is required. Same thing happens with other languages too. But Rust is quite young, even then we have abandoned projects. In 10 years I am not sure how Rust codebases will be more maintained not less. Now one can say maintenance point is about commercial projects where people are paid to maintain. Still then not sure how loved it would be by maintainers when original developers are long gone.
That's not been my experience. On a team I was on, even code from six months prior would sometimes be difficult to compile. They keep changing how GOPRIVATE works, or how modules work, or how vendoring works with modules.
Perl is a great example because although it has its quirks and the ecosystem shows some age, there really isn’t anything like Perl. It is a very good language for interacting in the console and with data of many types. It’s $_ variable which is based on implied context (much like in language we short-form speech when context is implied). Those are features something like rust (or go) simply isn’t designed for.
Hell, people still use and enjoy programming in C or Basic or Pascal. They just aren’t “popular” because developers are eager to put their comp-sci to use and invent more languages :)
A “simple” language frequently offloads more work onto its users. In my experience with golang, this is true. Meaningful abstraction is punished or impossible, so every solution to every problem—no matter how many times it’s been solved before—becomes a computer science exercise where every detail has to be dealt with and remains on full display. Reading golang is a chore because you can’t zoom out and look at the high-level flow, it’s only low-level minutiae at every level of the code. It’d be like an architect having to write the molecular layout of a skyscraper into the blueprints. Chemical formulae everywhere but no picture of walls, floors, or a building anywhere to be found.
Second, claiming golang has “low boilerplate” is the programming language equivalent of Stockholm syndrome. Look up any popular project. Something like fifty percent is
if res, err := func(…); err != nil {
return nil, fmt.Errorf(“Look ma, I’m a human exception handler: %w”, err)
}
That’s not an exaggeration. 50% seems to be the trend from the projects I’ve sampled. It actually makes it frustratingly difficult to follow what a function is supposed to do because the logic is lost in a sea of nearly-identical error handling. Spotting a bug in one stanza of code that’s in a sequence of nearly-identical visual blocks is hard because your brain starts glossing over the details that are repeated.Go has a LOT of boilerplate. And that is why I love it. No magic. No implicit behavior. No weird stuff that will bite me if I don't know that something in a completely unrelated file is changing the behavior of my code.
It allows me to write high quality code and care about all the millions of details I should be thinking about.
Yes, 50% of go code is about handling specific cases and errors. And that's a good thing, because unexpected cases and error handling is 50% of the work that a developer should be doing. But often it is just neglected or difficult to spot in other languages (until it bites you).
What it doesn't have is a lot of noise basically intended to keep the compiler happy, or implement common indirection patterns. Types are generally simple and can often be omitted. It's not traditional OO, so a lot of needless abstraction can be skipped. Complying with interfaces just means providing the required methods. Go code compared with the equivalent C or Java is denser.
For what it's worth there was an attempt at improving errors, which would have added a keyword that means "do this, or if it fails return the error result in this function's error result". I liked that idea. I'm not sure why it wasn't well received, but they evidently have that on their to-do list.
Why cram a Rust advocacy soundbite into a thread about maintenance vs. prototyping?
Rust's main claim to fame - safe manual memory - is already being re-examined. There is definitely interest in and pursuit of a lower-boilerplate means of getting the same, and there are some candidates out there already.
Further, a language being successfully used by experts and popularized tends to precede an accumulation of low quality contributions that negate base improvements to maintenance. In due course, a popular Rust will become "enterprised" with an accretion of tooling and frameworks - although it might not look the same as "enterprise Java", it will have the same kinds of effects in creating barriers to entry and sucking the sense of "fun" out of the day-to-day workflow.
Lastly, the application categories will change. There will be demand for "glue" again, applications that are just frontends to library code(perhaps code written in Go or Rust). The new language will be something not systems-focused, and perhaps aim to be mostly focused on content and presentation (as HTML was in inception).
What are some of those candidates?
But more importantly I disagree to your actual assessment:
Go code is very easy to read and understand. You're at the core of the problem at hand at once. No over abstractions or complex syntax or complex language features. Go has very few quirks (every language has them, people thinking "their" language is free of them are just being ignorant). I found very few real world issues caused by language quirks in Go production code (just as with Java or .NET).
Much more important for quality code are engineering decisions, structure and processes. As Go is easy to write, read and understand it leaves more mental capacity to those often underrated areas.
Go is a memory managed language. That alone makes it much easier to understand and maintain than every other language where you need to do it yourself. Again, more room for engineering decisions.
As Go has a very large and extremely useful standard lib you have much less issues with unmaintained third party libs. Imaging a large Rust codebase and all it's dependencies to one-person-maintained libs. A maintenance nightmare thinking 10 years ahead.
Just look at some good quality Go code, e.g. Hugo https://github.com/gohugoio/hugo/blob/master/navigation/menu... I've picked an arbitrary file. There might be issues in it, but look how easy to read and understand it is.
Regarding Rust: I have no general interest in talking bad about it, but need to make a point. Just as a real world reminder think of the Actix story. One part is the unsafe issue. How many people in the world would be able to understand the actual code and could have cleaned it up? Maybe 50? Fortunately there was a happy end. But how much time and energy did it take to get it on track again? What if Actix had not been the most popular and known framework?
All our companies Go servers (still mostly Java apps) are build with the standard lib. One thing less to worry about in the long run.
No language solves all problems obviously, but clearly some are better than others in nthat regard. I have no experience in Rust but many years with Perl and Java (and others). Perl in the hands of a small, very experienced team had the highest productivity and quality I have seen - but I would not want use Perl in a project with higher developer churn and lots of inexperienced coders.
This has been my experience as well.
I think that's why "medium powered" languages such as Python and Java tend to get the widest adoption over time. They are less expressive but also have fewer gotchas, and in a team of 100s or 1000s of good-but-not-stellar developers who come and go, they end up being the least painful for the organization.
> all languages are capable of producing maintainable software
Once you add that, you understand what the original poster actually meant.
Examples of such languages are Go and Python: both to some extent enforce code formatting - Python with indentation being part of the syntax, Go with gofmt being almost universally used tool. Both try to maintain one preferred way to do things - opposite to "There's more than one way to do it" in Perl.
Of these, (4) is by far the easiest part and the only one Rust will help you in. The digging for understanding is far more time consuming. The other parts require art, discipline, and linters.
Rust would not be friendly towards producing a RCA on this issue.
I'd also like to challenge the idea that Rust is optimized for maintaining. It's optimized for backwards compatibility. IMHO Ada is way better for long-term maintainability and in that respect beats every other language I know.
but... that's a good thing. programs should be built as a collection of small DSLs tailored to the specific tasks of that program
Of course, names or intentions of things don’t really determine their quality, or we’d all be running our software on the alpha processor, namely the DEC Alpha. My pedantic comment is worth about as much, but might be interesting to some:
Rust isn’t named after iron oxide, it’s named after fungus[0][1][2].
[0] https://stackoverflow.com/questions/16494822/why-is-it-calle...
[1] https://en.wikipedia.org/wiki/Rust_(fungus)
[2] https://www.reddit.com/r/rust/comments/27jvdt/internet_archa...
>> Rust on the other hand is optimized for maintaining
How come this post isn't flagged? Crazy ... How can someone more aggressively start a language flame war?
These kind of posts of an individual are the reason for the bad reputation of the Rust community.
Looking at the facts regarding the industrial usage and growth of Go and Rust in the real world one can only conclude that Go's honeymoon hasn't started yet.
There are so many reason why Go is much more likely to be maintainable 10 years from now:
- easier language
- massive standard lib
- professional maintainers at google
- garbage collector
- much, much more devs available
- very sensible maintainers to keep Go stable
- much more mature despite being not that much older
- ...
The Rust users seem to be spending all their time making Rust vs Go posts on the internet instead of making something killer enough to get people to use Rust.
It's Rust that carries Ruby's and Scala's legacy of putting everything into the language. There's no forethought if it's a kitchen sink
As for Rust. Is there inheritance in Rust? No. Overloading? No. Global or static (like Java static) variables? No. GC? No. Exceptions as the main error handling feature? No. N ways of casting to a different type? No. 5 or 6 ways of building dataclasses like in Python? No. 20 ways of packaging / building a project? Again - nope. I think this is already enough to say they didn't put everything into the language, and they are actually very careful not to introduce unnecessary features.
Actually I find Rust quite small, compared to e.g. Java or C++, but it has much better expressivity.
I think the name Rust is rather a pun because it was designed to be "close to the metal".
> Also, calling Rust a research language is funny to me because -- as its name reflects -- we've tried hard to avoid incorporating new technology into it. We haven't always succeeded at failing to be novel, but we have a rule of thumb of not including any ideas in the language that are new as of the past ten years of programming language research. The field of programming language is full of old technology that hasn't been put to use in solving problems that it's exactly suited for. The goals with Rust were to avoid reinventing wheels, and see what the past had to teach us.
I completely DISAGREE with this statement!
I have already seen many year-old Rust projects fail to build due to changes to language, compiler and core libraries.
Specially the debian/Ubuntu Rust packages have hard time compiling many recent projects.
The latter bit is the opposite, that is, old compilers can’t possibly know about new features, and that’s not a compatibility issue. That’s an older tool chain issue, or maybe a “community prefers newer tool chains than distros do” issue.
And that's only the first problem with maintaining old Rust projects. There are so many other issues, specially with projects created in very early Rust days.
Tell me, have you ever seen a go project failing due to similar issues? Python had this problem once (2 to 3) and they are still apologising for it.
(Not trying to start a language flame war. I like Rust and want it to succeed. But right now things are a bit chaotic)
The Python 2 to 3 situation is very different, that is about new versions not compiling old code. We go to great lengths to make sure that that works well in Rust.
When I first saw Rust 1.0 spec, I made a mental note that "this will end in tears". (Do tears rust metal?) I made a similar prediction during Scala's honeymoon days: "This will be the JVM's C++".
Go will have a Java-lite career trajectory: it has a pretty wide sweet spot (in various dimensions) that more than compensate for its shortcomings.
The actual issue in this industry has been, and remains, the fact that it requires workers with above average intelligence that are tasked with building repetitive glorified book keeping applications. And in every programmer with a heart-beat's career, there comes a time when job boredom unconsciously lends a seductive glimmer to the shiny new thing. Java, Ruby, and Go all were in fact adopted because of the ambitious, smart, but bored, geeks decided they needed a brand new toy. (So taking your honeymoon metaphor, these trends are like mid-life crisis for programmers.)
And, compounding the issue is the fact that those minding the programmers were and remain (in the best cases) mostly behind the curve -- they're in meetings all day, after all -- and are simply not in a position to authoritatively smack down "let's use X!" enthusiasms. (In the general case, they are entirely clueless.)
Rust, like Scala, and Haskell, add an additional hook in that they offer the programming world's version of 'intellectual virtue signaling'. Case in point being your bon mot of "optimized for building".
Seriously, that's the actual job description for 99% of worker bees in this field: building things!
So, I'll make the prediction that Rust will go the way of Scala and Haskell, and there will come a brand new shiny and intellectually satisfying new thing and we'll have HN (in a typically forgetful fashion) dissing Rust and heaping praise on the new comer. This too shall pass.
[p.s. I have a self-contract to only post 1 message in a 24 hour period on the entire internet and this was it for tonight. /over & out]
I don't think them smacking the "let's use x" is a good idea since it's the Devs who'll be writing the code.
The org needs a framework that answers: 1. How easy is it to train someone to use this stack? How long would it take for someone to start writing good code 2. How much time would it save us when building something new? 3. How easy is it for someone completely new to the codebase to start contributing?
1 and 2 can be easily measured within the org
You can use OSS as a proxy for 3
I think Go is in a honeymoon phase and people will hate it in 10 years; I think Rust will continue to be liked even when it's a legacy language (perhaps no language can continue being loved once it becomes "brown", but I think Rust will be a lot less dreaded than others).
Go, like Ruby (mentioned at the end of the article), is optimized for building. You can do things quickly and easily with minimal ceremony. Some people love that. But that's exactly the trait that's starting to weigh on Ruby, and it's exactly the trait that has put PHP and Perl in the "dread" category over time (remember when people were in love with those?).
Rust on the other hand is optimized for maintaining (it's right there in the name: software should be allowed to become old and "collect rust"). Rust APIs are very hard to misuse, everything is constrained and specified very explicitly. Documentation and testing live inline with your code. You know exactly what a piece of code can and can't do - you even know whether it's allowed to mutate an object you're passing it. And of course certain mistakes are impossible to make at all without (again, explicitly) dipping into unsafe { }.
This kind of forethought is what makes a language that survives the honeymoon phase.