6 ms·
I forgot: The tenet "accept interfaces, return structs" is violated all over by returning the `error` interface. IMO it's okay to make behaviour-exceptions sp
by the_gipsy 2y ago
I forgot:
The tenet "accept interfaces, return structs" is violated all over by returning the `error` interface.
IMO it's okay to make behaviour-exceptions specifically for error handling. Rust for example doesn't really have builtin behaviour exceptions specifically for errors, they're generic to sumtypes and just happen to work well for errors. But then in practice you must resort to thiserror or anyhow helper crates to deal with errors in anything but tiny programs.
If you do make behaviour exceptions for error handling, be honest and upfront about it. Don't say "errors are just values so just use them like regular vars" in docs, if then there are several huge exceptions (tuple-returns and breaking a core tenet). If you make exceptions then you might as well do them right, instead of half-assing them. I believe zig does it right, but I haven't gotten around to try it.
- Seb-C 2y ago"Do what I say, not what I do" is almost a design guideline of Go at this point, there are so many inconsistencies like this in the language design.
- 9rx 2y ago> The tenet "accept interfaces, return structs" is violated all over by returning the `error` interface. To be fair, that expression came from a blogger who often wrote about Go. It is not a tenet held by the Go project. In fact, Rob Pike has made clear that he considers it to be misguided advice. It is only violated in the same way returning Result in Rust violates the assertion I made up for this comment: Do not return Result.
- the_gipsy 2y agohttps://go.dev/wiki/CodeReviewComments#interfaces https://go.dev/wiki/CodeReviewComments#interfaces
- wbl 2y agoHow on earth is that violated by error? error is implemented by types all over the standard library and beyond and consumed by functions that wrap errors in the errors package. It's exactly an example of what you claim is violated. Even more obviously your link isn't talking about functions but packages. There are some violations out there but generally when included packages define interfaces they are ones that get consumed like in io or it's dissolution.
- the_gipsy 2y ago> The implementing package should return concrete (usually pointer or struct) types It's in the first paragraph. It goes on in the second: > Do not define interfaces on the implementor side of an API “for mocking”; instead, design the API so that it can be tested using the public API of the real implementation. And yes, io.Reader/Writer violate that too, because either the tenet is wrong or the design of interfaces in go is wrong. > Even more obviously your link isn't talking about functions but packages. It doesn't matter: your exporting errors behind the error interface, not your concrete error implementation. If you're just using one of the many ways to create stringly errors (!) like fmt.Errorf, you maybe don't notice but you are in fact returning interfaces all the time.
- 9rx 2y agoThe actual first paragraph states: > This page collects common comments made during reviews of Go code, so that a single detailed explanation can be referred to by shorthands. This is a laundry list of common style issues, not a comprehensive style guide. The document does not assert that you must not return interfaces or that it is incorrect to return interfaces. It only indicates that returning interfaces at inappropriate times has been a recurring issue found during code review. Sometimes returning an interface truly is the right choice, but when it isn't... Like most adages in programming, the aforementioned tenet holds validity in many cases, but, as always, "use your noggin" applies.
- wbl 2y agoIt does matter that packages and functions are different. It also matters what the io package actually does. https://pkg.go.dev/io https://pkg.go.dev/io . The io package has a very limited number of functions that return Readers. The vast majority of its functions take Readers or Writers as arguments and do useful things with them: e.g. Copy or LimitedReader. Most of the interfaces it defines (ReedSeeker, ReadWriteSeeker, etc) aren't instantiated by anything in it. os implements Reader and Writer for filehandles. net does the same for sockets etc.