1. 14
  1.  

  2. 2

    I’ve been away from the Go world for a few years now. I was excited when go fuzz came out; I missed the introduction of go mod and generics. I have confidence that they are well-designed, but I find the documentation… impenetrable.

    The article says go mod init is enough. Not sure what it does, but fine. We do loads of $TOOLCHAIN_NAME init these days.

    The term “module” is overloaded in the industry: is my application a module, or does it have modules? If my code is supposed to be modular, then maybe the latter? Let’s check the official docs. My options are either this 79-page-long reference, or a series of blog posts from four years ago that may or may not be up-to-date.

    I guess at least that tells me what the motivation for this was, but this sort of thing is why I don’t feel comfortable going back to Go, until another edition of the blue book comes out.

    1. 10

      Yeah, I’ve read all of that Go mod stuff, so I’ve internalized it, but it’s a lot. 😂 It doesn’t help that Go modules work significantly differently than other systems so if you just assume how it should work, you’ll probably assume wrong. That said, it’s actually pretty easy to use in practice and I can’t say I’ve run into any problems with it.

      A quick summary:

      If you just want to run one file that just uses the standard library, you can just do go run file.go. But if you want to have a collection of files that import other collections of files, you need to explain to the Go compiler how all the files relate to each other. In Go, a “package” is one folder of .go files and a “module” is a collection of packages. (This unfortunately is the opposite of Python, where a “module” is one file and a “package” is a folder or a collection of folders.)

      To start a Go project, run go mod init name and it will write out a go.mod file for you, or you could just handwrite a go.mod file. What should “name” be? Well, the name is going to be the import root for your project. If you’re not going to publish your project online, it should probably be something like go mod init janedoe/fooproj and then in your packages, you would do import "janedoe/fooproj/utils" to import your utils folder. When you start a spam project, you can do go mod init janedoe/spam etc. It’s probably not a good idea to just do go mod init bar because if some future version of Go adds “bar” to the standard library, you would have to change your module name. OTOH, you can just fix it when that happens. If you are going to publish your project online, the name should be a URL where the project can be downloaded. In most cases, one code repository should be one module and vice versa, although you are technically allowed to have multiple modules in a repository. If you need to work across multiple code repositories, there’s a “workspaces” mode for it. You probably won’t need it.

      The go.mod file contains the name of your project (module) and the names and version numbers of any third party packages you want to import. There’s a separate go.sum file that contains checksums for the modules you import. To add an import to your project, you can just add the import to a .go file like import "rsc.io/sampler" and then run go mod tidy and Go will download the project and add it to go.mod for you along with the checksum in go.sum. Alternatively, you can run go get URL@v1.2.3 and the import will be downloaded and added to go.mod as a pending import. Be sure to run go mod tidy again after you use the import in your project, so it’s moved to the list of used imports.

      Versioning is a little quirky. It just uses normal Git tags with semantic versioning, but when a project reaches v2+, the import path for a project has to include /v2 or whatever in it. The idea is that you are allowed to import github.com/whatever/project/v2 and github.com/whatever/project/v3 simultaneously, but you can’t import v2.0.1 and v2.0.3 simultaneously, so the major version number goes into the import path to be explicit about which one you mean. It’s a little ugly, but causes no problems if you don’t try to fight it. (Like a lot of Go.)

      And that’s basically enough to get started. The rest you can just look up in the go tool help and Wikis as needed.

      1. 1

        This is tremendously helpful, thank you!

        Versioning is a little quirky. It just uses normal Git tags with semantic versioning

        How much is go get and the toolchain in general coupled to Git as a VCS? IIRC, you could go get from HTTP endpoints in the past. I see that, in the rsc.io/sampler example you gave, there’s this line:

        <meta name="go-import" content="rsc.io/sampler git https://github.com/rsc/sampler">
        

        …which presumably is what bridges from HTTP to git to serve the content.

        But does versioning work in the absence of git?

        1. 3

          Go also supports Mercurial, SVN, Bazaar, and Fossil. There’s some process by which the VCS calls can be replaced with HTTP calls for efficiency. See https://go.dev/ref/mod#module-proxy Trying to understand all the specifics is how you end up with impenetrable 100 page docs. Mostly you can just ignore it assume it works somehow.

        2. 1

          Versioning is a little quirky. It just uses normal Git tags with semantic versioning, but when a project reaches v2+, the import path for a project has to include /v2 or whatever in it. The idea is that you are allowed to import github.com/whatever/project/v2 and github.com/whatever/project/v3 simultaneously, but you can’t import v2.0.1 and v2.0.3 simultaneously, so the major version number goes into the import path to be explicit about which one you mean. It’s a little ugly, but causes no problems if you don’t try to fight it. (Like a lot of Go.)

          Okay, so wait, you’re supposed to keep all sources in your repo?

          1. 2

            If I understand the question, no. Keeping your sources in your repo is called vendoring. There’s a mode for it in the go tool, but by default your sources are kept in a user cache directory somewhere.

            By git tag, I mean to publish a library, you just add a Git tag to your library repo and then consumers can just specify that they want that version. The versions must be semver style and when a library publishes v2.3.4, a consumer would import it with import "whatever/v2".

            1. 1

              You don’t have to, but it’s supported and encouraged - and I’ve been so pleasantly surprised how useful an approach it is that I’ve adopted it for projects in other languages also.

          2. 2

            go mod init just creates a file at the root of the current directory called go.mod, containing the name of your module (either pre-determined if you’re still creating projects in $GOPATH or specified when you run the command) and the Go version used to develop the module (i.e. the version of Go that is run when you use the go command to do things - this matters for the compiler mostly).

            As for if your application is a module or has modules - the answer is yes. It’s both. If you publish an application written in Go publicly that has a go.mod file, I can import it as a module into my own application.

            Other than that, modules are confusing and probably one of the more painful parts of Go, for me at least.

            1. 1

              A module is a thingy that lives in one place (probably git), that contains one or more Go packages, that has a version, and that can depend on and be depended on by other modules. That’s about all there is to it. You don’t have to do all that much to interact with them, particularly if you’re writing an app — just go mod init when you start (just like this thing says), and then whatever you go get while inside your app’s directory structure will be local to your app, and recorded in your go.mod so that you’ll get the same set of deps if you build it on another machine.