August 26th, 2024 08:19 UTC · 1 month ago

Golang

Relearning Go: Day 11

Reading Files & Writing Files to Exit

Reading Files & Writing Files

gobyexample.com/reading-files
gobyexample.com/writing-files

Simple and self-explanatory.

IO means checking errors frequently, which brings us into close contact with Go’s error handling story. Maybe there’s an axiom in this, like: the more verbose the language, the more programmers will take shortcuts – like panicking or ignoring errors. I wonder if this is borne out in practice.

Line Filters

gobyexample.com/line-filters

There’s some more ceremony to reading lines from files in Go than in other languages I’ve used.

Go’s Scanner is also kind of ugly, since errors are not propagated. Instead, it seems that you must check by calling Err explicitly after Scan returns false. There’s nothing in its design that leads you to remember to do this.

The Scanner.Text documentation also doesn’t mention what will happen if the bytes scanned are not valid UTF-8. The signature is func (s *Scanner) Text() string so errors are not propagated this way. Maybe the string returned can be invalid UTF-8? Or should we also check Scanner.Err?

File Paths & Directories & Temporary Files and Directories & Embed Directive

gobyexample.com/file-paths
gobyexample.com/directories
gobyexample.com/temporary-files-and-directories
gobyexample.com/embed-directive

Nothing to add.

Testing and Benchmarking

gobyexample.com/testing-and-benchmarking

Nothing much to add, but I thought this was cool:

t.Run enables running “subtests”, one for each table entry. These are shown separately when executing go test -v.

I assume the test code is not compiled into a release build, but I’m not sure of the mechanism.

Command-Line Arguments & Command-Line Flags & Command-Line Subcommands

gobyexample.com/command-line-arguments
gobyexample.com/command-line-flags
gobyexample.com/command-line-subcommands

Almost universally the world has converged on the -s short and --long argument pattern, which appears to come from a combination of the POSIX conventions for command line arguments combined with a GNU extension for long options1. This was the prevalent style even in the 2000s when Go first appeared.

The flag module uses a different, outdated pattern for arguments. Tooling created with flag will work differently to what the majority have come to expect. For those that aren’t aware of the distinction, it sows confusion. For those that are, it sows distrust. It undermines the usefulness of the command-line.

It’s a small corner of the language, but it feels needlessly contrary to promote this superseded style by bundling it in the Go standard library – meaning it can not be updated until at least a hypothetical Go 2. There are third-party packages to parse arguments that are in line with modern (i.e. since the 1990s) expectations, but not everyone will know they exist.

Environment Variables

gobyexample.com/environment-variables

Makes sense, but Go ignores that environment variables are byte strings, and not necessarily UTF-8.

Logging

gobyexample.com/logging

Nothing to add.

HTTP Client

gobyexample.com/http-client

Nothing much to add, except that the use of defer in the example means the HTTP request could be kept open longer than it needs to be.

HTTP Server

gobyexample.com/http-server

Useful to have in the standard library. Like its counterpart in the Python standard library, I suspect it doesn’t get used much for “real” work though.

Context

gobyexample.com/context

Nothing much to add, except that the API is confusing. When do I use the various methods? The documentation says what they do, but not when and why I should use them. Context feels like a grab bag of a few things that were missed when designing channels and goroutines but couldn’t be added because they were already set in stone.

Spawning Processes

gobyexample.com/spawning-processes

The API is familiar.

Unlike the “system” library call from C and other languages, the os/exec package intentionally does not invoke the system shell and does not expand any glob patterns or handle other expansions, pipelines, or redirections typically done by shells.

Good stuff; system(3) is a blight.

The example ignores so much error handling. It’s almost as if Go’s idiomatic error handling gets in the way of comprehension.

Exec’ing Processes

gobyexample.com/execing-processes

The example notes:

Note that Go does not offer a classic Unix fork function. Usually this isn’t an issue though, since starting goroutines, spawning processes, and exec’ing processes covers most use cases for fork.

Interesting decision, no complaints (for once), but the next example is a very UNIX subject:

Signals

gobyexample.com/signals

Signals come in on channels! Nice indeed!

Exit

gobyexample.com/exit

Nothing to add.


With that, we’ve completed Go by Example.

For sure I had much to complain about in Go, but Go by Example was a great primer. It kept the pace up, but also piqued my interest such that I experimented with the examples, and frequently went off to read other Go documentation, especially at pkg.go.dev.