ProgrammerHumor

zeroInitEverything

zeroInitEverything
https://i.redd.it/ruw16kywyi9f1.png
Reddit

Discussion

Therabidmonkey

I'm a boring java boy, can someone dumb this down for me?

3 weeks ago
theschis
:ts:

Uninitialized variables aren’t undefined, they’re zeroed. Hilarity ensues.

3 weeks ago
Kinexity
:cp::py:

What's the problem with that?

3 weeks ago
SaneLad

It's better than nothing, but only marginally so - which seems to be the entire design philosophy behind Go.

3 weeks ago
Axman6
:hsk:

The entire philosophy behind Go is “developers are dumb so they can’t have nice things, but we’ll make them think it’s a nice thing by having fast compile times”. The amount of time it took to add generics is just inexcusable, I remember when Andrew Garrand came to my uni when Go first came out and being asked about it. But, they already had generics, but you’re too dumb to be allowed to use them.

Also, every fucking second line being error handling is absolute insanity. It’s a testament to just how poor the ability to build abstractions are (give me a monad for f’s sake).

There’s no language that makes me more angry than Go, there are other languages which have their own quirks, but they often have the benefit of “we don’t know better”. Go’s developers did know better, and decided “we do know better” - the arrogance and assumption that all developers are dumb AF is just insulting. I would say that Go just feels like a Google product, but it actually feels like an Apple product, you have to use it their way because you’re too dumb - ironic given that Swift seems to actually be a pretty nice language.

Defer is nice though.

3 weeks ago
Conscious_Switch3580
:cp::c::ru::perl::hsk::asm:

every fucking second line being error handling is absolute insanity.
Defer is nice though.

yeah, error handling is pretty bad. after using Zig for a while, defer feels half-assed without errdefer.

3 weeks ago
2Uncreative4Username
:c::g:

Go has errdefer, though - you give the returned error a name and then defer a func that operates on that. It's a pattern you'll find everywhere in the stdlib. The issue with people hating on Go often comes down to people superficially seeing it looks like languages they already know and then thinking they know how to program in it idiomatically. Go definitely has its issues, but most of the points mentioned here resolve themselves if you actually learn how to use the language.

3 weeks ago
Conscious_Switch3580
:cp::c::ru::perl::hsk::asm:

that’s just shabby. compare to something like this:

``` errdefer result.deinit();

if (foo) return error.Foo; if (bar) return error.Bar; // …

return result; ```

3 weeks ago
2Uncreative4Username
:c::g:

I know - yours is simpler for your use-case. You rarely need errdefer in Go, and for when you do need it, Go's version is more flexible. For example, in Go you can check what the type of error is. That's what I mean: You shouldn't argue about this stuff if you haven't at least put some effort into learning how Go does things. A good starting point is reading the std library, IMO.

3 weeks ago
Conscious_Switch3580
:cp::c::ru::perl::hsk::asm:

that doesn’t make any sense. you know you can use additional logic before the return, right? and why would you need to check the type of error when you exactly which one you’re returning?

speaking of handling errors, this is also cleaner than Go’s patterns:

``` const myVal = try foo();

const myOtherVal = foo() catch |err| { // … }; ```

3 weeks ago
bakaspore
:lsp::rust:

The amount of time it took to add generics is just inexcusable

It's more about incompetence. They admitted that without contacting Phil Wadler (who uncoincidentally did generics for Java) they don't even know what is a correct design, let alone implementation.

source: https://commandcenter.blogspot.com/2024/01/what-we-got-right-what-we-got-wrong.html?m=1

3 weeks ago
Axman6
:hsk:

As you’d expect, Haskellers, who spend a lot of time thinking about types and semantics, save the day again. It’d be nice if more languages had Haskell’s level of type system power, being able to write abstractions over data types is something I miss very often. classes like Foldable are a great example, which give you functions like toList :: Foldable t => t a -> [a], is so useful. It’s like being able to say in a C++ esque syntax List<A> toList(<T><A> t) where A can be everything (and unlike C++ templates, there isn’t one implementation per T and A, only one per T).

3 weeks ago
bakaspore
:lsp::rust:

Oh the good ol' HKTs. Meanwhile in Go: we don't have generic parameters on methods yet, we don't know how to do that again plz help (and refusing actual help from community)

Btw you don't need that to have a toList like function that practically works, see collect and FromIterator in Rust.

3 weeks ago
zelusys

Defer is nice though.

Oh no it isn't. Defer in Go doesn't defer to the end of the block, it defers to the end of the function. What in the fucking fuckage fuck is this incompetent level of design decision-making.

3 weeks ago
freekarl408

Why is that incompetent? Defer should only be used for cleaning up resources, like closing a file handle, or a network connection.

3 weeks ago
zelusys

Because deferring to the end of the function instead of the end of the enclosing block goes against human intuition and the syntactical structure. Assuming designers of Go have no human intuition, the mismatch with the syntactical structure should have stood out. This language is a joke.

3 weeks ago
dlg

Scoping defer can be hacked in by using an immediately invoked function expression, but it’s ugly and unintuitive.

2 weeks ago
reddi7er

is that a legal rule - to have block scoped defer? different language different choices. same in spoken language also- some do subject+verb+object some do subject+object+verb in a sentence

2 weeks ago
Plazmatic

Why is that incompetent? Defer should only be used for cleaning up resources, like closing a file handle, or a network connection.

This is like saying "Why shouldn't you eat shit? You would eat a burger right? What about pizza? Sometimes shit is made of those things."

Like yes, just like you might be fine eating a burger, but you wouldn't eat shit, you use defer to clean up resources and defer should still work at the block scope level... you know... to help you clean up resources. What if you want to manage a resource within an if statement, a for loop? Any {} block you create? You know, how RAII normally works and defer is meant to emulate?

2 weeks ago
freekarl408

I don’t understand your “shit” analogy.

If you want to release resources at the scope level, utilize the io.Closer interface and call it at the end of the scope.

2 weeks ago
Plazmatic

If you want to release resources at the scope level, utilize the io.Closer interface and call it at the end of the scope.

My dude, you have to be joking here.

6 days ago
Axman6
:hsk:

Oof, ok it’s been a long time since I looked into the specifics of defer. I guess you’d want some kind of control over where it defers to?

3 weeks ago
chat-lu
:rust: :elixir-vertical_4: :re: :clj: :py: :kt: :j: :bash: :js:

The problem is that the zero value of many things is nil. Which means that your zero valued array will crash at runtime.

It would be more sensible to use default values instead of zero values. An array default value would be an empty array.

Also, having everything nullable is called the billion dollars mistake for a reason, it’s unexcusable to put that in a programming language designed this century.

3 weeks ago
Responsible-Hold8587

It's funny you use "nil arrays" as an example. Arrays can't even be nil because they are fixed size and all indexes are initialized with the zero-value for that type. There's no such thing as a zero-valued array crashing at runtime.

Besides that, you almost never use arrays directly in go. You typically use slices, which are dynamic views backed by arrays.

There's also no such thing as a runtime crash caused by a slice being zero valued. Go effectively treats nil slices the same as an empty slice. You can check the length, iterate, and append just fine. Trying to read a value from a nil or empty slice will both panic, which is the correct behavior because there are no values at any index.

In practice, you don't see a lot of null pointer exceptions in go like you would in many other languages, since methods can be called on nil pointers (including slices), and errors are handled as values so it's extremely obvious when you're writing bad code that doesn't handle and error and may try to interact with a returning nil pointer.

Maps though, you can read from a nil map but not write to one. This is the source of most nil pointer exceptions I've seen and maybe one of the few times I wish for default values.

3 weeks ago
VisibleMoose

For nil structs what I see bite people is methods that can return a nil struct AND a nil error, but that’s just poor code like you said.

3 weeks ago
Responsible-Hold8587

100%, I never do this and I always ask for it to be fixed in code review.

Functions should return a valid value XOR an error. Never nil, nil. In extremely rare circumstances, I'll allow value and error but it has to have a strong justification and has to be very clearly documented.

Edit: okay, one exception allowing `nil, nil` is when nil is valid for the type, like a nil slice, but that's uncommon for a struct. When returning a map, my non-error path would always return an initialized map.

3 weeks ago
syklemil
:rust::py::bash::hsk:

Functions should return a valid value XOR an error. Never nil, nil.

In that case you should return a type that holds a valid value XOR an error. Unfortunately the Go devs chose a pseudo-tuple that holds a potentially garbage value AND a potential error value. So they wind up permitting nonsense combinations, and proceeding with garbage values if the error checking is buggy or absent.

So I and a lot of other people agree with you on returning valid values XOR errors, but as far as Go is concerned, that's, just, like, our opinion, man.

3 weeks ago
nobrainghost

I dont think that guy's ever touched Go

3 weeks ago
LoyalOrderOfMoose

I've touched Go a bit (member of the Go team, author of the slog package) and I agree with u/Responsible-Hold8587. If you're getting a lot of NPEs, perhaps you're holding it wrong.

3 weeks ago
nobrainghost

Hey, I'm not disagreeing with him, rather adding to what he said. It is in reference to the guy he is addressing. /u/Responsible-Hold8587 is absolutely right in his explanation. Sorry for the misunderstanding

3 weeks ago
Responsible-Hold8587

It's all good! I understood it :)

3 weeks ago
LoyalOrderOfMoose

Oh! Sorry about that.

3 weeks ago
Responsible-Hold8587

Thanks! And thanks for making slog :)

3 weeks ago
arobie1992

The regularity with which I've seen complaints dismissed as the person not using the language right when the language's stated goal was to be simple to learn and use is truly disheartening.

3 weeks ago
LoyalOrderOfMoose

Fair point. I took the original comment as dismissive (I was wrong, see above) and matched that tone. It would have been better to try to educate.

But also, Go has footguns and pitfalls and flaws, like any complex tool. And there are certainly ways to hold it wrong, and a learning curve for some things—like nil maps, which were a problem for me for a few months until I internalized the concept.

3 weeks ago
Some_Confidence5962

Could you not make the same "if you see this error lots you're doing it wrong" about virtually any poorly designed language feature anywhere?

No language is fool proof. That's for sure. But my point is that if people keep falling in a trap, then at some point you must blame the trap not the people falling into it.

2 weeks ago
IngrownBurritoo

The guy above explains everything right and thats your response? You need to touch go again it seems

3 weeks ago
nobrainghost

Its not him, its who he is adressing

3 weeks ago
IngrownBurritoo

Then I stand corrected. Thanks

3 weeks ago
LoneSimba

You sure you're responding to the correct message?

3 weeks ago
nobrainghost

My bad, a demonstrative pronoun misunderstanding, it's in reference to the guy he's addressing

3 weeks ago
Some_Confidence5962

In practice, you don't see a lot of null pointer exceptions in go like you would in many other languages, since methods can be called on nil pointers (including slices)

That's certainly NOT my experience with it. Possibly could blame my coleague for that but it hits null pointers just as easily as many other languages.

Yes a nil pointer can be passed to a method if the method accepts a pointer (not all do). But go makes is really unclear when you are dererencing a pointer. It has no . vs -> as seen in C/C++ and this leads to a heap of cases where you thought you were safe from nil pointers but infact you accidently dereferenced one.

Nil pointers are bad, and many languages now force you to declare when you wish to allow one. Sadly not go.

2 weeks ago
myka-likes-it
:cs::js::unity::unreal::gd::cp:

Best thing that ever happened to C# was fixing their default nullability of types.  Writing my own null checks everywhere, or just hoping I made null values impossible, was the worst part of using that language.

3 weeks ago
chat-lu
:rust: :elixir-vertical_4: :re: :clj: :py: :kt: :j: :bash: :js:

It seems that they borrowed Kotlin’s fix. Good idea, Kotlin did a great job.

3 weeks ago
myka-likes-it
:cs::js::unity::unreal::gd::cp:

As someone learning Kotlin right now, I can't disagree.

3 weeks ago
depressed_koala2

The methods and operators working with slices eg. len and range do handle nil slices just fine, so don't see it as much of an issue there.

The default nullable behavior of structs does result in panics sometimes, but it also makes us think more about handling such cases carefully.

3 weeks ago
kilkil

which means your zero valued array will crash at runtime.

In Go, nil slices are explicitly considered identical to empty slices, so this is false.

... however, it is 100% true for maps. 😭

3 weeks ago
CrowdGoesWildWoooo

The out of the box vscode extension will tell you if a variable is never used and it helps a lot at spotting dangling lvalue.

Honestly I don’t see a reason why you want a pure uninitialized value. The only reason is if I want a placeholder or if I need to do something that accumulates as I go. So I would usually assign a default anyway when initializing a variable.

Also for arrays and map, it’s not really a weird behaviour at all, because arrays are pointers with offset. What’s the “best” uninitialized value of a pointer that doesn’t point to anything?

3 weeks ago
LoneSimba

Unused or not initialized? Unsed vars will prevent code from compiling all together, wouldn't they?..

On arrays - 'pure' go arrays are never nil, since they have defined length and all keys are initialized with zero values of T inside it (var arr [5]int), but people most often use slices , which are objects with an underlying array pointers, and on length 0 there is basically no array inside it, so reading from a zero slice will result in NPE (rather, go handles this and rather that panic with NPE they tell you explicitly that index X is not in the slice, and will provide slice's length, which for zero slice is 0)

3 weeks ago
CrowdGoesWildWoooo

Go array declaration is nil. It’s because it is not assigned a capacity yet. This is the “var a []int” to be exact. This is because the array is not allocated just yet and it is nil (although if you print you get something like empty array).

Go array is dynamic As soon as you add your first element, it will dynamically grow the slice. As long as the bounds checking is satisfied.

3 weeks ago
LoneSimba

It's not an array, it's a slice. Arrays in go is fixed, and declared by "var [x]T" where x is desired capacity (i.e. var pos [3]float64), x can be replaced with double dot if you're assinging values via short declaration (pos := [..]float64{3.5, 10.22, -3.2}), see https://go.dev/doc/effective_go#arrays and https://go.dev/doc/effective_go#slices and https://go.dev/play/p/gQtIYGVQlIG

3 weeks ago
arobie1992

In certain languages, a truly uninitialized value will trigger a compilation error if you try to reference it. I wanna say Java does this sometimes and Rust does it all the time, but I'm hazy on both. This can even extend to cases where the variable is in an indeterminant state per static analysis.

For example:

Bar foo
if(isTuesday()) {
    foo = new Bar(50)
    foo.print() // everything is fine
}

foo.print() // compilation error: "Cannot call functions on an uninitialized variable: Variable 'foo' may not be initialized depending on execution path."

The idea is that even the trivial fix of explicitly setting it to null in a language without static null safety makes mistakes less likely because the value is readily apparent.

3 weeks ago
beaureece

Not everything is nullable. It's pointers, collections, and interfaces.

3 weeks ago
reddi7er

this. zero values can be leveraged to your ease once you get the hang of it. only problem here is some of the zero values are nil which is horrible

2 weeks ago
[deleted]

[deleted]

3 weeks ago
StoneAgainstTheSea

From a purity standpoint, you may be tempted to default to doing that nil receiver check. In practice, most structs are initialized via some constructor, like 'NewMyThing(...) MyThing', and it is a safe assumption that a method will only be called on a non-nil receiver.

I have worked on dozens of production grade Go services and it simply isn't an issue.

3 weeks ago
_Meds_

Been using Go for 8 years on profession payment services. I've literally never thought about this. Y'all are doing something wrong, and I don't even know how you're getting there? A lot of the time it's because you're trying to write C or Java with Go syntax, which obviously doesn't work, but then you complain that it doesn't work?? Just use C or Java, what's wrong with you people, lol

3 weeks ago
[deleted]

[deleted]

3 weeks ago
_Meds_

This is literally just showing you that a pointer, even a method receiver “CAN” be nil, but you wouldn’t really nil check in the method, you’d do it on the creation of the type, which it would have also shown I’m certain.

This is how teaching works. It’s not telling you to copy this basic pseudocode into all your projects.

3 weeks ago
LoneSimba

Lear to learn, then. Or read other materials, like https://www.gopl.io/ - a nice read, for both newcomers and experienced devs alike

3 weeks ago
LoneSimba

By arrays you mean arrays or slices? Arrays are defined as var arr [x]T, where x is length and T is type, and they are never nil, see https://go.dev/play/p/t30Mv-nYfhD Slices (var slc []T) on ther hand are in fact objects, wrapping an underlying array pointer, and are nil by default (since there is no array by default), https://go.dev/play/p/E7Ru2DasL15

It is pointed out to in docs, afaik, https://go.dev/doc/effective_go

3 weeks ago
Ok-Possibility-1353

if you need empty array there is a make function 😭please dont comment any thing without having knowledge of the language

11 hours ago
chat-lu
:rust: :elixir-vertical_4: :re: :clj: :py: :kt: :j: :bash: :js:

if you need empty array there is a make function

I know that. It’s a shit design.

10 hours ago
Ok-Possibility-1353

if you pointing design, i can agree that creating empty array we need to use make fn, so yeah it is a shit design, but yeah every lang has its pros and cons

10 hours ago
chat-lu
:rust: :elixir-vertical_4: :re: :clj: :py: :kt: :j: :bash: :js:

The thing that annoyed me the most about Go is that it insists that every variable be used. No chill out mode when you are not done writing that function. Other langages just warn.

10 hours ago
Ok-Possibility-1353

but why u use something that not gonna used in programme, if still you want to store something and dont want to use it or access it u can store it in blank identifier

10 hours ago
chat-lu
:rust: :elixir-vertical_4: :re: :clj: :py: :kt: :j: :bash: :js:

I’m gonna use it. I’m not using it now because the function is not done.

1 hour ago
DirectInvestigator66

It’s better than the alternative but not perfect and a large majority of the industry is more used to the alternative.

3 weeks ago
BosonCollider

Also a huge amount of Go code relies on it to handle optional fields in APIs, with zero fields in struct being used to denote missing values, in a way that sometimes conflicts with what you would expect

3 weeks ago
arobie1992

This is another big problem with zero values as universal defaults. What if the zero value is a valid value, but you want to make sure the user made a selection? Well, gotta change the design of your struct or include documentation to inform the user of what they need to do for the state to be valid.

3 weeks ago
BosonCollider

The flipside of this from an ops point of view is that eventually you get used to all Go related tooling with marshalled configs having this quirk, so while it is not great it is not great in a very standardized way, and it is sort of nice from a blub studies point of view.

3 weeks ago
BroBroMate

How do you tell the difference between "not set" and "explicitly set to 0"?

There's workarounds.

3 weeks ago
DanielMcLaury

What does that have to do with the type system?

3 weeks ago
Divingcat9

In Java you get nice stack traces showing exactly where things broke. In Go, errors are just values you have to manually check everywhere, so when something fails you're basically debugging Rob Pike's minimalist philosophy instead of your actual bug.

3 weeks ago
GodsBoss

Ah yes, the 30-line (MineCraft) or 100-line (Keycloak) stack traces which contain dozens of uninteresting pass-through methods, just to not tell you where the real error occured. I also prefer these over Go's errors containing five handcrafted unique error messages which are extremely easy to find and already tell you what happened in a human-readable way.

3 weeks ago
LoneSimba

If your code is not using any sort of reflections it might be true. But go's error handling is way more fine-grained - most of the times error message is somewhat unique and you can easily find it in your project code, unless your writing 100500 same messages for empty arrays or something

3 weeks ago
kerakk19

And I wrong or you make it sound like Java error handling is somehow better?

Go expects you have actual error handling, not idiotic try catch or other horrors some langues do.

If that's too much, you can use panic and recover to have your precious Java behavior.

The only language that's better than go at error handling is rust. Every other is inferior.

3 weeks ago
Ready-Desk

"nice stack trace" has to be a troll

3 weeks ago
crystalchuck

Until you use a framework or working on an OOP'd out the ass application and you have so many layers of reflection and magic that you can't even make sense of the stack trace an eigth of the way down anymore

3 weeks ago
CrowdGoesWildWoooo

It’s a good pattern because it forces you to do error handling. You can actually ignore it, but that means you are deliberately not catching an error.

Unless you are using a library built by second rate developer, this is pretty much one of the most acceptable pattern, so it’s on you whether to continue to use that pattern in your downstream code or simply skip the error checking altogether.

3 weeks ago
Thenderick
:g:

What's wrong with that? I like that feature, because it does make sense. Coming from other languages it will take a little while to get your head around it, but I don't see any downside to it. The only reason I can think of you don't want this is when a function fails to Get something and usually returns null (or nil in this case), but that is instead solved by Go's multiple return value system where you simply return an additional boolean value to indicate success.

What I do hate about this zero value system is that it makes sense 95% of the time. Numbers? Zero. Boolean? False. String? "". Pointer (or a reference type like interface)? Nil. Struct? A struct with all fields zeroed. A built-in hashmap where you have already specified the key and value type? An empty map? HAHAHAHAHA no fuck you, nil! That is the only one that annoys me. I understand that it has to do with maps being stored as a reference/pointer type instead of a value type, but it pisses me of a little sometimes...

3 weeks ago
0x564A00 OP
:cs::rust:

There are indeed a ton of cases where having a default value makes sense, and in many cases zero is even a good value! But other times there is no logical default value – what, for example, is a default user or a default window handle – or the sensible default isn't simply zeroes, or maybe you need to track something for all instances of a type but anyone can create an instance of any type out of thin air as easily as declaring a variable.

Many other languages don't have this problem. If in Haskell I want to produce a value of a type, I have to call one of its data constructors.

But really, the unavoidable zero-initialization is just one aspect. Go also makes all references nullable, lacks sum data types (or even enums, despite adding a partial workaround for them), has two different ways an interface can be null (which makes returning a concrete error type a footgun ), has tuples but only in the form of multiple return values (which are a workaround for the lack of sum types: functions that either succeed or fail still have to return both a success value and a error value (just with one of them set to nil)), no controls around mutability, a rather unfortunate list implementation (and I'm not referring to the memory unsafety here).

In general, a lot of it comes of as if the design choices were made not according to what would be most useful for language users, but what could be implemented without much research into other languages.

3 weeks ago
chat-lu
:rust: :elixir-vertical_4: :re: :clj: :py: :kt: :j: :bash: :js:

Many other languages don't have this problem. If in Haskell I want to produce a value of a type, I have to call one of its data constructors.

In Rust if it has a default value, then it implement the Default trait. You can implement it yourself for your own types. If you try to get the default value out of a type that doesn’t have one, the compiler will have your back and point out the issue.

3 weeks ago
hans_l

Just want to point out that there is no “default value” when declaring a variable in Rust, you have to assign it a value, so you can call a constructor just like Haskell. It’s just that you can use the constructor Default::default() if your type supports the trait. Also, it is possible to initialize a variable (any, including undefined behavior) with uninitialized memory using MaybeUninit::uninit().assume_init() (which is unsafe).

3 weeks ago
chat-lu
:rust: :elixir-vertical_4: :re: :clj: :py: :kt: :j: :bash: :js:

Yes, you have to choose to use the default value explicitly. But that’s a separate concern from them existing in the first place.

3 weeks ago
LittleMlem

I've become somewhat of a go fanboy recently. I think the design philosophy is that you should make "constructors" for custom types. What ticks me off is that the constructor can't be a dispatcher on the actual type so you end up with a bunch of LOOSE NewMyType functions

3 weeks ago
ignat980
:g::c::py::lua::js::sw::math::bash::powershell::asm::cp::cs::j::

Well, you are right that a zero value is not always useful. The Go team's guiding idea is initialization safety: that every variable has a well-defined state the instant it comes into scope. That choice trades some expressiveness for ergonomics. You can drop a bytes.Buffer, sync.Mutex, or http.Server literally anywhere, and it "just works". When the zero value is meaningless (for example, *os.File{} or time.Time{}), the idiom is to expose helpers like os.Open or time.Now so callers cannot create a useless value by accident, while still letting power users build structs by hand if they really want to.

About Nullable references; yes, any pointer, map, slice, channel, or interface can be nil, and that can sting sometimes. The counterpoint is that most code does not need pointers. Structs and slices are cheap to copy, and when you pass a non-nil slice or struct, the compiler guarantees it is usable. For truly non-optional references, provide a constructor that returns a concrete (non-pointer) value so nil cannot escape the function.

For sum types, enums, tuples... generics and type sets in Go 1.18+ do not give us algebraic data types, but they let you express many "sum-ish" constraints without reflection. Still, pattern matching on tagged unions is nicer :) The multiple-return "error last" style is a poor man's Either, but it keeps the happy path free of exceptions and, combined with defer, produces very linear control flow. Whether that is a net win is a matter of taste; I think it is.

For mutability controls, Go relies on copy-by-value, intentional use of pointers, and API design (exported vs unexported fields) instead of const or readonly. Not perfect, but in practice you see ownership rules during code review because they are spelled out in the signatures, not hidden behind extra keywords.

The list package exists mostly for completeness and those rare cases where you need stable cursors; otherwise it is a relic from the pre-slice era. Just use slices.

Now the main part, a non-nil interface can still hold a nil pointer, and invoking a value-receiver method on that pointer will, of course, panic. You returned *DatabaseError directly to highlight the foot-gun; see this for the idiomatic fix. Return error, use errors.As or errors.Is, and the panic disappears. I prefer the "return error" route because it keeps the public API small, yet still lets callers recover the concrete type when they care.

In short, Go's design optimizes for simplicity, tooling, and mechanical sympathy with the garbage collector, sometimes at the cost of the expression power you might find in Haskell, Rust, or newer Java and C# features. That can be frustrating when you want fancier type machinery, but it pays off in readability, onboarding speed, and low cognitive load once a codebase reaches "large messy company" size.

3 weeks ago
Responsible-Hold8587

There's a lot of nonsense criticism from other people in this thread that seemingly know nothing about go. I appreciate that this comment is dripping with thoughtful experience in go and many other languages. The issues you've pointed out can definitely cause pain and discomfort and I've experienced most of them at one point or another :)

I'm curious if you could explain more what you meant by this part?

>a rather unfortunate list implementation (and I'm not referring to the memory unsafety here)

3 weeks ago
0x564A00 OP
:cs::rust:

In other languages any mutation you make is either visible to anyone else who holds that list, or this shared mutation isn't possible in the first place.

Go's slices share an underlying non-resizable buffer. This means that if you mutate a slice, the mutation might be visible to other slices using the same buffer or it might not. For example the output of the following snippet depends on the value of capacity:

go a := make([]int, 0, capacity) b := append(a, 1) _ = append(a, 2) fmt.Println(b)

Relatedly, you usually can't index out of bounds… but you can create a subslice that goes past the original slice's length (as long as it stays in capacity). For extra funny results, append to the original slice afterwards :)

As for the memory unsafety reference: Slices aren't thread-safe, so writing to a slice (not to it's shared buffer, that is) while also accessing it from another goroutine can result in a mismatch between observed data pointer and capacity, so you have accidental out of bounds reads/writes. Luckily that doesn't happen too often.

3 weeks ago
Responsible-Hold8587

Good points, thanks!

I'm aware of those slice issues but surprisingly don't run into them often. I guess that there haven't been many cases where I had multiple different slices to the same data being held and used by different variables.

For thread safety I would always wrap concurrent slice access in a mutex, but it's cool that you wouldn't have to do this in other languages.

3 weeks ago
anotheridiot-
:g::c::py::bash::js:

It's the perfect grug brain language, i like it.

3 weeks ago
RiceBroad4552
:s:

No, it isn't.

Grug knows, Go is stupid.

Grug is very smart!

If you disagree, Grug is reaching for club!

3 weeks ago
LoneSimba

 what, for example, is a default user or a default window handle

you usually store such objects as a pointer, so default is nil. lack of enums and zero maps being nil (rather than a map with length of 0 like slices) aside, there is nothing that really bothers me.

which makes returning a concrete error type a footgun

there is a set of functions in errors package to check what kind of error it is - https://pkg.go.dev/errors#As, https://pkg.go.dev/errors#Is

no controls around mutability

you mean the fact, that Go is not a OOP language? it is based of good ol C (also made by Rob Pike), so there are lots of similarities

or do you mean there is no readonly? that is somewhat sad, but before such keyword existed in other languages people usually made such a field private and made only getter method for reading it, allowing to initialize it only during construction

3 weeks ago
chat-lu
:rust: :elixir-vertical_4: :re: :clj: :py: :kt: :j: :bash: :js:

where you simply return an additional boolean value to indicate success.

The poor man’s algebraic type. Had they included the real thing, it would have solved their nil problem at the same time.

I understand that it has to do with maps being stored as a reference/pointer type instead of a value type, but it pisses me of a little sometimes...

It has more to do with their shoddy design and picking zero values instead of default values.

3 weeks ago
Harry_Null

0001-01-01 00:00:00 +0000 UTC

3 weeks ago
killbot5000

You can read from a nil map with no issue.

3 weeks ago
nobrainghost

the most likely next block you write will be a error handler which will catch that nil!

3 weeks ago
New_York_Rhymes
:g::ts::cs:

I hate this almost as much as values being copied in for loops. I just don’t get this one

3 weeks ago
L33t_Cyborg
:table:

Pretty sure this is no longer the case.

3 weeks ago
Mindgapator

What? How would they change that without breaking like everything?

3 weeks ago
Chuu

fwiw, C# also made a breaking change to how foreach loops and lambda expressions work because the default was the opposite of how people intuitively thought it should work. Sometimes it's worth the pain.

https://stackoverflow.com/a/8899347

3 weeks ago
BosonCollider

They ran the change with test cases from the entire google source code repository, and got only two failures, both of which were assert fail tests. The entire Go ecosystem was basically carefully avoiding the default behaviour

That was convincing enough that they decided to ship it, and a very good case for the original design being awful.

3 weeks ago
Responsible-Hold8587

I'm not sure what you mean. What change was made recently that means loop variables are no longer copied?

In this snippet, changing values in the loop does not update the actual array because the loop var is a copy of the value, not a reference.

https://go.dev/play/p/mI9fshO7VVZ

func main() { vs := []int{1, 2, 3} for _, v := range vs { v += 1 // Updates a local copy, not the value in the slice. } fmt.Println(vs) // out: [1, 2, 3] }

The only thing I can think of is the loopvar change they made for goroutine closures in 1.22, but that change made it so values that were previously copied into the same space in memory (overwriting each time), now occupy unique positions in memory. Eiher way, the loopvar is still a copy.

https://go.dev/play/p/O1s7POEB-OS

``` // In <1.22, the code below usually prints '9' ten times. // In >=1.22, it prints 0-9 in a randomish order.

func main() { var wg sync.WaitGroup wg.Add(10) for i := range 10 { // Not capturing i. go func() { fmt.Println(i) wg.Done() }() } wg.Wait() } ```

3 weeks ago
nobrainghost

To avoid memory churn is the goal I think. The goal is to stay cheap and that's a trade off

3 weeks ago
Sobriqueter

Aren’t strings also reference pointer types essentially? Seems inconsistent

3 weeks ago
nobrainghost

Making it a reference makes it easier to copy around since its just 16 bytes no matter how long it is

3 weeks ago
killbot5000

A 0 length string whose data pointer is null is still “”.

3 weeks ago
Sobriqueter

Sure, but there’s still a nun-null struct that houses that data pointer, similar to how an empty hashmap might have null data.

3 weeks ago
PeksyTiger

In before (real) generics

3 weeks ago
18441601
:c:

1.18 and onwards have real generics

3 weeks ago
PeksyTiger

Generic methods when?

3 weeks ago
BosonCollider

Go is not really the language for advanced types, and trying to extend it to do that means that more people who would not enjoy Go may end up writing Go instead of a language they would prefer

I'm fine with Go as a domain specific language for webservers and for devops tooling. I like it in that usecase but idk if I want it to spread into other niches. Dart and Go being very different is a great example of why different kinds of languages are useful in different niches imo

3 weeks ago
Aidan_Welch
:g:

Literally part of time formatting

3 weeks ago
kintar1900

In This Post : LOADS of developers who try to use Go like Language X, then complain that it wasn't designed to be used that way.

3 weeks ago
HQMorganstern
:j:

Every Go discussion ever:

- *Legitimate language question raised by some random CompSci student who's only ever written C++"
- STOP writing GO like JAVA, you DON't need a framework for this, roll your own auth !!1!

3 weeks ago
Plazmatic

This is Go evangelists fault and Googles fault, because they both try to advertise Go as a general purpose language, when it isn't, then people try to do what Google themselves imply you can do with the language, and it falls flat on it's face because it was made for servlet applications and webservers, not for graphics programming or systems programming.

2 weeks ago