Noob question: trying to understand modules

I am trying to make a module that (for now) contains only one file with one function that returns an integer. So far I have this:

package foopkg

func Foo() int {
    return 42
}

I put this into a file foo.go

and this is in a folder mod2

I issed the commands

go mod init mod2
go install mod2

What I expected was that it installs this in my GOPATH into the appropriate folder and from within another project I can then

import “mod2/foopkg”

But this doesn’t work.

Obviously I have completely misunderstood and confused everything, otherwise it would work.

  • Can I have modules that do not have main and do not build an executyble?
  • Can I have module names that do not point to any public host or repository on the internet?
  • Does install actually do what I assumed it does?
  • What would I need to do to make this work?

mod2 istn’t even a valid module name. As far as I remember a module needs to begin with some “domain name” like segment that signifies its hoster.

Anyway, when using modules then GOPATH is only used for some caching, it won’t be used for look up stuff.

Yes. Modules can contain library packages. In fact, this is the main purpose of a module – to organize packages into versioned libraries.

Short answer: Yes, but…
Long answer: Modules were designed with package avilability in mind. Hence by default, module paths and import paths should point to remote repositories.

However, even if you do not have your library module on any remote repository, you still can pretend that you do.

  1. Use a module path that points to a remote repository. It does not need to exist.
  2. Use the replace directive to tell the Go toolchain that the actual module lies somewhere on your local filesystem.

Example: Say your module path is “github.com/yourorg/yourmodule”.

Your Go code uses import "github.com/yourorg/yourmodule" as import path, just as if the module and its package were available remotely

Then call

go mod edit -replace "github.com/yourorg/yourmodule" => "your/local/path/to/yourmodule"

Now your go.mod file contains a hint to fetch the module and its package locally.

Advantage: Once you are done with local development, you can then push the module to the remote repository, remove the replace directive in go.mod (you can use -dropreplace for that), and you’re done. No need to change all the import paths from local to remote.

go install compiles a binary and installs it into $(GOPATH)/bin. It is of no use for library modules.

See above – use remote module paths and go mod edit -replace.

1 Like

Thank you for your answer. I am slowly making progress, the replace directive in the go.mod might prove to be helpful (I hope).

I can partially understand why it has been designed as it is, but some of it seems overly restrictive to me. After googling around for the topic of “relative imports” I can find many discussions with strong opinions for and against such desire, and while I can somehow understand why that might be restricted when referring to other modules (as in my case above) I have meanwhile stumbled across something else that I believe is restricted too much for no obvious (to me) reason at all: relative imports INSIDE the same module.

Consider this

example.com/mymodule
  |_ main.go (package main)
    |_ sub
      |_ sub.go (package sub)

if I wanted to refer to the package sub from within main I am not allowed to just import “./sub” for some reason I have yet to understand this is not allowed.

However, after playing around with replace I came up with the following:

replace _ => ./

and now I seem to be able to import “_/sub”, so I can at least refer to the module root without knowing its name, I can go run or go build the module and it does not complain.

Will this cause me trouble later down the road? If none of these internal sub-packages is ever supposed to be used standalone and merely exists for namespacing and code organization reasons, is there any scenario where this would lead to problems?

This is an interesting hack indeed! As per the docs, replace requires a module path and a replacement path as arguments around the => string. “_” is not exactly a valid module path, hence I would not count on go mod to support this trick forever.

The question I would ask myself in this situation is:

If I do not plan to make a sub-package available to other projects, would I need that sub-package at all?

Especially for small projects, you do not necessarily need to break everything into packages. Instead, you can start by adding everything to package main first and refactor later. And if any piece of code then seems worth going into a separate package with a well-defined package API, then the package might be worth living as a standalone package and being available for other projects through its own fully qualified import path.

Hence the need of fully qualified import paths may seem a restriction at first but may pay off in the long run.