Golang: Import Module from Sub Directory

In Golang, importing modules is easy, however it has some opinions on how to structure your project. You're not supposed to sort the files of your project into different directories, but to keep your files in the root folder of your repository OR split out functionality into other modules.

However, it's not a cop, so you can still use the package field and an import path to import modules from a sub-directory.

Go Idiomatic Directory Example

Let's create an ice cream shop that makes use of an icecream.go model and some basic price calculation.

main.go

package main

import (
	"fmt"
)

func main() {
	myIcecream := Icecream{Flavour: "vanilla", Scoops: 3, BasePrice: 2.99}
	fmt.Println(myIcecream.Price())
}

icecream.go

package main

type Icecream struct {
	Flavour   string
	Scoops    int
	BasePrice float32
}

func (i Icecream) Price() float32 {
	return i.BasePrice * float32(i.Scoops)
}

directory structure:

.
├── main.go
└── icecream.go

1 directory, 2 files

We can confirm that this works by running go run main.go icecream.go and it should print: 8.97.

Importing Go Files from Sub-directory Example

If we should decide to structure our projects with sub-directories, we need to make some changes. First of all, we should make sure to have a go.mod file that states the name of our package, so let's run go mod init, which will by defautl take the name of the directory as the module name.

go.mod

module icecream-shop

go 1.23.1

Next we can create a directory, like models and move our icecream.go file there.

directory structure:

.
├── go.mod
├── main.go
└── models
    └── icecream.go

2 directories, 3 files

If we try to run our code with go run . now, we'll get the following error:

# icecream-shop
./main.go:8:16: undefined: Icecream

Alright, let's now try to import it, we need to remember that unlike in for example JavaScript, we don't import specific files but modules, which are directories:

main.go

package main

import (
	"fmt"
	"icecream-shop/models"
)

func main() {
	myIcecream := models.Icecream{Flavour: "vanilla", Scoops: 3, BasePrice: 2.99}
	fmt.Println(myIcecream.Price())
}

Note:

  • add "icecream-shop/models"
  • change Icecream to models.Icecream

However, we get another error if we try to execute it with go run .:

main.go:5:2: import "icecream-shop/models" is a program, not an importable package

Lastly, we'll need to change the package name of the file we moved into the sub-directory, otherwise we'll not be able to import it and use it:

icecream.go

package models

type Icecream struct {
	Flavour   string
	Scoops    int
	BasePrice float32
}

func (i Icecream) Price() float32 {
	return i.BasePrice * float32(i.Scoops)
}

Note:

  • we changed package main to package models, signalling that this is its own module

Summary

If you're structuring your project, evaluate carefully if the functionality you're moving around should in fact be its own package or repository.

If not, remember to use the correct import paths.

Tagged with: #golang #go

Thank you for reading! If you have any comments, additions or questions, please tweet or toot them at me!