Every type in Go has a zero value. The zero value for pointers is nil. So you can't do it with regular pointers, because users can always create an instance of the zero value.
This is one of those things which feels like just a small trade off against convenience for the language design, but then in practice it's a big headache you're stuck with in real systems.
It's basically mandating Rust's Default trait or the C++ default (no argument) constructor. In some places you can live with a Default but you wish there wasn't one. Default Gender = Male is... not great, but we can live with it, some natural languages work like this, and there are problems but they're not insurmountable. Default Date of Birth is... 1 January 1970 ? 1 January 1900? 0AD ? Also not a good idea but if you insist.
But in other places there just is no sane Default. So you're forced to create a dummy state, recapitulating the NULL problem but for a brand new type. Default file descriptor? No. OK, here's a "file descriptor" that's in a permanent error state, is that OK? All of my code will need to special case this, what a disaster.
…and now you need to check for nonsense values everywhere, instead of ever being able to know through the type system that you have a meaningful value.
It’s nil pointers all over again, but for your non-pointer types too! Default zero values are yet another own goal that ought to have been thrown away at the design stage.
> instead of ever being able to know through the type system that you have a meaningful value.
That's... not what I'm looking for out of my type system. I'm mostly looking for autocomplete and possibly better perf because the compiler has size information. I really hate having to be a type astronaut when I work in scala.
So, I mean, valid point. And I do cede that point. But it's kind of like telling me that my car doesn't have a bowling alley.
But it does not work. It looks like it would, with the indentation and iota keyword, but its just some variables that do not constrain the type. There will be incoming rogue values, from json or sql or something else.
var g Gender // ok so far.
if err := json.Unmarshal("99", &g); err != nil { panic(err) }
// no error and g is Gender(99)!
Now you must validate, remember to validate, and do it in a thousand little steps in a thousand places.
Go is simple and gets you going fast... but later makes you stop and go back too much.
Default gender male not how this works in practice. Instead, you define an extra “invalid” value for almost every scalar type, so invalid would be 0, male 1 and female 2. Effectively this makes (almost) every scalar type nullable. It is surprisingly useful, though, and I definitely appreciate this tradeoff most of the time.
(Sometimes your domain type really does have a suitable natural default value, and you just make that the zero value.)
This is a thread about Go, not about Rust. There is a bunch of interesting computer science in this post, and if interesting new computer science is a baby seal, Rust vs. Go discussions are hungry orcas.
Given the choice between a (objectively) theoretically superior language like Haskell or Rust , and a language that prioritises developer ergonomics at the expense of PL research, I'll take the ergonomics thanks.
We have a 1MM line c++ codebase at work, a rust third party dependency, and a go service that's about as big as the rust dependency. Building the Rust app takes almost as long as the c++ app. Meanwhile, our go service is cloned, built, tested and deployed in under 5 minutes.
It's not "a bit faster", it's "orders of magnitude faster". We use a third party rust service that compile occasionally, a clean build of about 500 lines of code plus external crates (serde included) is about 10 minutes. Our go service is closer to 5 seconds. An incremental build on the rust service is about 30s-1m, in go it's about 5 seconds. It's the difference between waiting for it to start, and going and doing something else while you compile, on every change or iteration.
> what ergonomics does Go give you that Rust doesn't
- Compilation times. See above.
- How do I make an async http request in rust and go?
in go it's
go http.Post(...)
In rust you need to decide which async framework you want to use as your application runtime, and deal with the issues that brings later down the line.
- In general, go's standard library is leaps and bounds ahead of rust's (this is an extension of the async/http point)
- For a very long time, the most popular crates required being on nightly compilers, which is a non-starter for lots of people. I understand this is better now, but this went on for _years_.
- Cross compilation just works in go (until you get to cgo, but that's such a PITA and the FFI is so slow that most libraries end up being in go anyway), in rust you're cross compiling with LLVM, with all the headaches that brings with it.
- Go is much more readable. Reading rust is like having to run a decompressor on the code - everything is _so_ terse, it's like we're sending programs over SMS.
- Go's tooling is "better" than rust's. gofmt vs rustfmt, go build vs cargo build, go test vs cargo test
For anything other than soft real time or performance _critical_ workloads, I'd pick go over rust. I think today I'd still pick C++ over rust for perf critical work, but I don't think that will be the case in 18-24 months honestly.
And yet it's a productive language with significant adoption, go figure.
They made trade-offs and are conservative about refining the language; that cuts both ways but works well for a lot of people.
The Go team does seem to care about improving it and for many that use it, it keeps getting better. Perhaps it doesn't happen at the pace people want but they always have other options.
> And yet it's a productive language with significant adoption, go figure.
Perl was also a successful language with significant adoption. At least back then, we didn’t know any better.
In twenty years the industry will look back on golang as an avoidable mistake that hampered software development from maturing into an actual engineering discipline, for the false economy of making novice programmers quickly productive. I’m willing to put money on that belief, given sufficiently agreed upon definitions.
I can't agree with the definitions you're insinuating. To suggest that the creators of Go do not know what the difference is between "writing software" and doing "software engineering" is plainly wrong. Much of the language design was motivated by learnings within Google. What other "modern" language that has more of a focus on software engineering, putting readability and maintainability and stability at the forefront? It's less about novice programmers and more about artificial barriers to entry.
Modern PLT and metaprogramming and more advanced type systems enable the creation of even more complex abstractions and concepts, which are even harder to understand or reason about, let alone maintain. This is the antithesis of whatever software engineering represents. Engineering is almost entirely about process. Wielding maximally expressive code is all science. You don't need to be a computer scientist to be a software engineer.
> In twenty years the industry will look back on golang as an avoidable mistake
And here is my opinion:
I think in 20 years, Go will still be a mainstream language. As will C and Python. As will Javascript, god help us all.
And while all these languages will still be very much workhorses of the industry, we will have the next-next-next iteration of "Languages that incorporate all that we have learned about programming language design over the last N decades". And they will still be in the same low-single-percentage-points of overall code produced as their predecessors, waiting for their turn to vanish into obscurity when the next-next-next-next iteration of that principle comes along.
And here is why:
Simple tools don't prevent good engineering, and complex tools don't ensure it. There are arcs that were built in ancient Rome, that are still standing TODAY. There are buildings built 10 years ago that are already crumbling.
Perl had less competition and also suffered from being more of a "write-only" language.
Lisp, Haskell, OCaml all likely tickle your PL purity needs, but they remain niche languages in the grand scheme of things. Does that make them bad?
I think Go will be the new Java (hopefully without the boilerplate/bloat). It's good enough to do the job in a lot of cases and plenty of problems will be solved with it in a satisfactory manner.
Language wars are only fun to engage with for sport, but it's silly to get upset about them. Most languages have value in different contexts and I believe the real value in this dialog is recognizing when and where a language works and to accept one's preferred choice may not always be "the one".
One answer would be to provide something like a GetPointer() method which, if the inner pointer is nil, creates a new struct of type T and returns a pointer to it.
It's basically mandating Rust's Default trait or the C++ default (no argument) constructor. In some places you can live with a Default but you wish there wasn't one. Default Gender = Male is... not great, but we can live with it, some natural languages work like this, and there are problems but they're not insurmountable. Default Date of Birth is... 1 January 1970 ? 1 January 1900? 0AD ? Also not a good idea but if you insist.
But in other places there just is no sane Default. So you're forced to create a dummy state, recapitulating the NULL problem but for a brand new type. Default file descriptor? No. OK, here's a "file descriptor" that's in a permanent error state, is that OK? All of my code will need to special case this, what a disaster.