10 ms·
Go’s Features of Last Resort
- varshithr 7y agoI am not very familiar with Go but panic() and recover() seem like neat features.
- paol 7y agoIt's just exception throw/catch. The only difference to other languages is that you're not supposed to use it, as a matter of principle.
- saturn_vk 7y agoRather, it should (not must) be used for exceptional cases, like if something happens and you have no idea how to deal with it.
- aikah 7y agoYou're not supposed to use struct tags, type aliases, "interface {}" or reflection either... that's a lot of features Go developers "aren't supposed to use"... Why are they here then?
- eternalban 7y agoThese features do not scale so one should take care to use these language features only when necessary. Also the OP is not Gospel of Go, merely an expressed opinion.
- sturgill 7y agoFrequent use of these patterns is a good smell test that one should reconsider design choices. Are there times when usage is justified? Yes. Should it be the first / default mechanism? No. Learn the rules first, and then it will be more clear when you should break them. I have no problem with the general rule nor a built-in mechanism for breaking them. Simple example: if I ever have a row lock I wrap the corresponding block in recover to ensure that I never have a deadlock. But I certainly don’t wrap every function in recover...
- Carpetsmoker 7y agoThere's no problem with using any of those features; they just come with some drawbacks and in many cases they're not the best tool. But in some cases they are, and then by all means go ahead and use them. They exist for a reason.
- frou_dh 7y agotry/throw/catch work at block-level. panic/recover are not an option until function-level
- tgv 7y agoThey're not meant to be used like that (and terminate the function, not the block), so the implementation can have more overhead than try/catch. What bothers me about it is that there's no syntax for it: recover as it is looks like a dirty hack. I've been writing my first production software in go this year, and I must say I'm quite favorable towards its error handling now, ugly as it may look. Even during testing there haven't been any crashes except in the places where you can expect them (like writing x := y.(string) when y isn't a string). For me, the only real downside in go: nil pointers in combination with the absence of non-nullable types.
- weberc2 7y agoThis is my experience after 7 years of Go as well. Error handling is actually fine. People make too big a deal about the error check (and Rust people make too big a deal about the compiler not forcing you to handle every error). The substantial opportunities to improve error handling involve establishing a standard, typesafe interface for getting at the relevant, structured information in an error so callers can handle things properly; however this probably depends on generics and sum types and will probably look a lot like Rust’s result types (which seems great to me). In practice these are not where your bugs are. On the other hand, sum types would actually go a long way in reducing bugs (including the nil pointer problems you mention).
- vlowther 7y agoThe only time I have used recover in production code is in the rollback path of a highly concurrent transaction manager.
- Carpetsmoker 7y agoIt's not really, because there is no straightforward way to express something like: def f(): bar() try: foo() catch KeyError as exc: print(exc) Calling recover() in a defer applies to the entire function, so you'll need to wrap foo() in an anonymous function. You also need more work to not use "Pokemon exceptions" (gotta catch 'em all) and recover() only KeyError
- frou_dh 7y agoSay you put a single type Foo in its own package to ensure that nothing ever — intentionally or unintentionally — touches its unexported (private) fields. If "Dot Imports" are frowned upon, it means that your other code using that type is always going to have to refer to it as the stuttering "foo.Foo" rather than "Foo". That's not great.
- zeveb 7y agoPutting a type into a type-specific package like that is considered (IMHO at least) unidiomatic in the first place, so this isn't a situation which can happen in practice.
- frou_dh 7y agoWhat's the counterargument to the expressed benefit?
- klodolph 7y agoIt breaks the ability to Ctrl-F to find uses of the package foo. It’s surprising to other Go programmers. If you’re working by yourself, go wild and do what you want. On teams, I’m much more likely to need to search through somebody else’s file to understand it or find something. It also means that additions to foo might conflict with definitions in your package. The reason this feature exists is to break circular dependencies in unit tests. So in foo_test, you might import ( . "foo" "fooutil" )
- frou_dh 7y agoThis makes sense but it's more like general advice for why typical packages should not be import-dotted. I was talking about a very specific kind of low-pollution package that will export essentially nothing bar a single type (maybe a NewFoo constructor too). The package isn't a bucket, it's a barrier. (I hope people move beyond ctrl-f/grepping, to tooling that has semantic understanding of definitions/references. Go go gopls & GoLand!)
- leetrout 7y agoAs a very opinionated user of Go for ~6 years now... interface{} is almost always a code smell. I can judge a book by its cover with a grep and line count for interface{} and reflect. If it's more than 20~30 for just about any small-medium size code base I dig deeper and look at why. Usually it is people being lazy and refusing to accept what is my twist on "idiomatic Go": a little bit of copying is better than a whole lot of abstraction[0]. Also devs telling me interface{} and type switches make it polymorphic are the ones who will always complain yet not leave the Go ecosystem. Hopefully generics in 2 solve this for everyone. panic is great when you really want to crash. There are times to do this. I have only used it once in a batch job runner. init() is a sin ESPECIALLY when you combine init() with the `.` (dot) imports. Pulling packages around for their magic side effects suck. Also the lack of some library maintainers to acknowledge init() can be poisonous... [1] [0] https://go-proverbs.github.io/ https://go-proverbs.github.io/ [1] https://github.com/lib/pq/pull/455 https://github.com/lib/pq/pull/455
- frou_dh 7y agoDid you mean init combined with import underscore?
- wsy 7y agoI agree with the assessment of interface{}. However, the proverb you cite does not exist. It is: "A little copying is better than a little dependency.", which does not apply here.
- parsimo2010 7y agoThat it why GP said it was their twist.
- ben_jones 7y agoTwo valid use cases I've found for interface{}: Handling large arbitrary JSON objects where I need to operate on their keys but don't need to work with their values by unmarshaling to map[string]interface{}. The alternative would likely be code generation of struct definitions with JSON bindings, which is likely preferable if you already have code generation in your project, but can be an overhead in smaller ones. It can also be used to create your own syntactic wrappers around API's which consume interface{} such as encoding/json and database/sql: Unmarshal(r *http.Response, dst interface{}) ([]byte, error) and BulkInsert(tableName string, columns []string, values [][]interface{}) (time.Duration, error)
- grenoire 7y agoStruct tags in Go always seemed like a very bad and hacky way to add metadata to fields. I really hope that's something they'll look into in Go 2.
- seiferteric 7y agoCurrently I am facing an issue where I am unmarshalling json into a protobuf struct. Unfortunately the json tag names are a bit different than the protobuf struct field names. I would love to be able to use struct tags here, but to my knowledge protobuf definitions don't allow this, and I don't think I can put them in after the fact. So I am stuck loading the data into an interface{} and manually pulling out the fields.
- zetetic 7y agois this for the protoc generated code? otherwise, could you not define different struct tags for the different serialization types? type Person { Name string `protobuf:"name",json:"given_name"` }
- seiferteric 7y agoYes it is for the protoc generated code. It would be nice if the protobuf spec allowed for something like tags as well. The main difference is the json coming in uses hyphens, whereas protobuf<->json only allows for underscore or camelCase field names.
- q3k 7y ago> Yes it is for the protoc generated code. It would be nice if the protobuf spec allowed for something like tags as well. Have you seen Protobuf options [1]? They allow for a tag-like-behaviour, but are strongly typed. It's notably used by gogoproto [2] to allow specifying custom field types or names [3] when generating Go stubs. [1] - https://developers.google.com/protocol-buffers/docs/proto#customoptions https://developers.google.com/protocol-buffers/docs/proto#cu... [2] - https://github.com/gogo/protobuf https://github.com/gogo/protobuf [3] - https://godoc.org/github.com/gogo/protobuf/gogoproto https://godoc.org/github.com/gogo/protobuf/gogoproto
- fortytw2 7y agoempty interface{} and struct tags really shine when used as a library author, or authoring a bit of extremely reusable code that’s pervasive throughout a codebase. Think things like json encoding (entirely réflection based), or writing a set of functions that can operate on something more generic. It’s not scary and it’s not really that slow, you just need to be very particular and choosy in your application of it.
- ArtWomb 7y ago>>> pass a map[string]string to json.Marshal() This is my goto speed hack when prototyping. It always works. You don't have to worry about deep nesting. And it frees you to design the data model as you prototype. It's also less punishing than reflecting on every node in a list. And yes, golang / json (and go / node) interactions need to evolve better developer patterns and runtime performance ;)
- NicoJuicy 7y agoI'm always wondering how .net developers feel towards Go. Anyone can relate?
- pjmlp 7y agoIt is a good replacement for C like programs, that is all.
- SamWhited 7y agoAnd also Python like programs. And probably Node.js like programs. And really anything where you have lots of I/O to do.
- pjmlp 7y agoI would rather use PyPy in Python's case.
- SamWhited 7y agoI would not because I still have to deal with the language and tooling, even if the implementation is much faster.
- pjmlp 7y agoDealing with the language is exactly why I would rather advise to use PyPy, instead of completely change ecosystem. Go would not even be on my list for Python developers looking for performance. There are much better AOT compiled languages with a feature set similar to Python.
- SamWhited 7y agoSuch as? Go is what Python should have been in my mind, although I would love a more expressive type system. But overall it generally replaces Python for me.
- papito 7y agoIt's very interesting to me how bad design decisions in Go are accepted by the users with little protest. There are other things - the Go path, error handling, surprising conventions in a language with no "magic", etc. Yes, it was created by behemoths of computer science (but more accurately in this case, dinosaurs), and they clearly have not been writing and debugging large production systems for decades now. That's why the whole thing is clearly missing the lessons learned from modern language design.
- SEJeff 7y agoWell they did port dl.google.com from a very large C++ codebase to go. Do you have any apps you've worked on that are higher traffice than dl.google.com?
- papito 7y agoI didn't say Go is unusable - I said it's full of bad design decisions. Google has a LOT of people who are Go fanboys. It's their in-house language. Terraform is also written in Go, and even though I refer to it to see how they manage a larger Go codebase, my question is - "so"? This is my opinion from writing Go, I have no problems pointing out what I don't like, even though at Google, I am sure, it would be unforgivable blasphemy.
- azth 7y agoThat's not an argument. There are many websites that are higher traffic, yet written in slow languages like Ruby and Python.
- hacknat 7y agoI would add the sync package to this list. Maybe not the same extreme, but if you force yourself to use channels you’ll end up writing more maintainable code.
- SamWhited 7y agoI would have said the opposite. Channels are great on occasion, but half the time I see them used in places where a simple mutex would be much cleaner and easier to read or where a mutex could be hidden from the user entirely but a channel is used instead and has to be exposed to the user. They each have their place. There are specific things in the sync package I see abused frequently, but I wouldn't call use of the sync package itself code smell.
- gautamcgoel 7y agoI think the fallthrough keyword also belongs in this category. It's a surprisingly ugly feature of the Go language, and I recall an interview where two of the three language designers said its their least favorite part of the language.
- luord 7y ago____ Exceptions are generally considered to not be worth the cost ____ Why is exception handling being added in 2 then? Sure, they're calling it check/handle instead of try/except(catch) because Go ought to be hip and different but if it moves like a duck and quacks like a duck...
- Carpetsmoker 7y agocheck() and handle() have rather different semantics; it's still just error returns, just with a bit of syntax sugar. It's also just a proposal which received a lot of feedback/criticism, and eventually they decided to go with another try() proposal which ended up being abandoned after criticism. I'm not quite sure what the current status is here. So nothing is "being added" in Go 2 thus far.