Relative imports

October 23, 2024

Newcomers to Go often try to use relative imports, and then are usually bitten by random weird problems. They seem to work sometimes, and not other times. What’s the deal?

import "./foo"

Import declarations

The interpretation of the ImportPath is implementation-dependent but it is typically a substring of the full file name of the compiled package and may be relative to a repository of installed packages.

Okay. So this bit of the spec doesn’t help a whole lot. It raises more questions than it answers. Are relative imports supported or not?

The short answer is: Sometimes.

Per the quote above, the meaning of an import path is implementation-dependent. And while I’d love to say that the official Go implementation has a definitive answer, it’s not quite that simple.

In practice, relative imports are supported in some contexts, but not all tools work well with them.

I might go to the effort to catalog which tools do and don’t work with them, but that’s a lot of work, and historically has been a moving target. So the much simpler answer, both to provide, and to implement, is simply: Don’t use relative imports!

Actually, that’s an over-simplification. Let me make it more explicit, with some good rules to follow:

  • When importing a package, always use the fully qualified import path. That is <module name>/<path>. That includes packages in your own module. <local module>/<path> will always work. ./<path> will sometimes work, but don’t rely on it. It’s ugly and confusing to read, to boot.
  • When naming a module, always use the fully qualified name where the module can be downloaded. github.com/user/module, for example. Not simply module. Technically, the latter works for modules that are never imported by another package. But it’s good practice to use the full name anyway, as it’s more clear, and leaves the option open to import it later, without the trouble of renaming. An unqualified name could also conflict with future standard library changes, if a standard library package with the same name is ever added. So all around, best to use the fully qualified name.

Quotes from The Go Programming Language Specification Language version go1.23 (June 13, 2024)


Share this

Direct to your inbox, daily. I respect your privacy .

Unsure? Browse the archive .

Related Content


Working with versioned imports

We’re continuing today our discussion of imports. Yesterday we left off with using explicit package names in an import clause when the package name differs from the last element of the import path. Is that the only reason to explicitly name your imports? No. Sometimes you need to disambiguate between two imports with the same package name. Or maybe you just like a shorter name. import ( log "github.com/sirupsen/logrus" // Shorter names are nice stdlog "log" // Oh, but we need to reference this package, too ) There is one exception to the rule “Name your packages the same as the last element of your import path,” though: v2+ releases.


Import cycles

First off, the spec provides an example of how to reference a symbol in an imported package, using different import alias options. I’ve already provided my own examples earlier, and it’s pretty straight forward, so we’ll just breeze through this part. Import declarations … Consider a compiled a package containing the package clause package math, which exports function Sin, and installed the compiled package in the file identified by "lib/math". This table illustrates how Sin is accessed in files that import the package after the various types of import declaration.


Valid import paths

Today we’re looking at one of the more essoteric parts of the spec. Import declarations … Implementation restriction: A compiler may restrict ImportPaths to non-empty strings using only characters belonging to Unicode’s L, M, N, P, and S general categories (the Graphic characters without spaces) and may also exclude the characters !"#$%&’()*,:;<=>?[]^`{|} and the Unicode replacement character U+FFFD. Other than what’s stated in the quote, we’re not going to look at an exhaustive list of what is and isn’t guaranteed to be supported by a Go implementation.

Get daily content like this in your inbox!

Subscribe