The first problem when trying to learn golang that I encountered is wrapping my head around how packages and import work in golang. Here is what I discovered and hope that others can benefit from it.

Let’s start 3 files that we want to migrate from python to golang

# hello_world.py
import world
import lib.adder

print("Hello {0}".format(world.world()))
print("1 + 1 = {0}".format(lib.adder.add(1, 1)))
# world.py
def world():
    return "World"
# lib/adder.py
def add(x, y):
    return x + y

File structure

.
├── hello_world.py
├── lib
│   └── adder.py
└── world.py

In golang the code will be

// main.go
package main
import (
    "adder"
    "world"
    "fmt"
)

func main() {
    fmt.Printf("Hello %v\n", world.World()
}
// world.go
package world

func World() string {
    return "World"
}
// adder.go
package adder
func add(x int, y int) int {
    return x + y
}

The code is pretty straight forward, the hard part is how to put them in the right place to get the import to work.

Understanding $GOPATH

GOPATH is the paths where golang look for libraries and modules. In python, it is somewhat similar to the virtualenv in python but with a few differences. If you come from C/C++, you can think of it as the path that you look for your headers.

One key thing to know is that you can have multiple path in your GOPATH, separated by : .

All GOPATH have 3 folders.

  • bin for the compiled binaries
  • src for the source code
  • pkg for the compiled libraries

Most tutorial in golang will say “treat your $GOPATH like your working directory” and I fell into the trap thinking that this is virtualenv; it is not.

We start by having a global $GOPATH, let use “{HOME}/.go” This is a simple way to start but as python developer, we don’t install packages to our global space. The only packages that we install into global python environments are the tools that we run, like virtualenv and pip or other python command line tools. For packages, we have virtualenv.

So here is what I am (probably) going to do. My experience with golang is limited, and thus is not the perfect solution.

Folder structure

So first, let’s assume that adder is an external library that we are importing and world is a module that we wrote.

Here is the final folder structure.

.
├── lib
│   └── src
│       └── github.com
│           └── zwodahs
│               └── adder
│                   └── adder.go
├── main.go
└── vendor
    └── world
            └── world.go

First, we assume that adder is at github.com:zwodahs/adder. To install, we just need to perform go get github.com/zwodahs/adder. What golang do in this situation is to find the first path in the $GOPATH and install adder to it.

To achieve what we want, we just need to export GOPATH as "$(pwd)/lib:$GOPATH". Once we set lib as the first GOPATH, the rest will be taken care by go. You can name lib anything you want, like genv for example.

For world.go, it is a bit trickier. The known solution is to put world inside vendor. However, this will require the working folder to be part of a GOPATH like this

. $GOPATH (any gopath will work)
├── pkg
└── src
    ├── {workingdir}
    │   ├── lib
    │   │   └── src
    │   ├── main.go
    │   └── vendor
    │       └── world
    │           └── world.go
    └── other workspaces or libs ..

I haven’t found a way to get around this yet. In python, we can put a project folder anywhere and it will work. Apparently, this is not the case in go, at least I haven’t found a way.

After playing around with it even more, the following is also possible

. $GOPATH (any gopath will work)
├── pkg
└── src
    ├── workspaces
    │   └── {workingdir}
    │       ├── lib
    │       │   └── src
    │       ├── main.go
    │       └── vendor
    │           └── world
    │               └── world.go
    └── other workspaces or libs ..

package What?

After I figured out where to put things, I wonder why there is a need for package world; so I started tinkering.

So here is what I found.

// main.go
import (
	...
	"world"
)

// world.go
package bigworld

The first thing I did was changing world to bigworld in world.go. Interestingly, it started failing at

    fmt.Printf("Hello %v\n", world.World()

informing me that world not defined.

After playing around more, I realized that by changing to package bigworld, go imported it as bigworld instead of world

Note that you can overwrite this by using the following import statement, which is very similar to python’s import ? as ?.

//main.go
import (
	world "world"
)

The second thing I tried is to change the folder name to bigworld.

As expected, we can’t import “world” any more. We will need to import “bigworld”. world.world() still works, since I didn’t change package world in world.go

That’s all for packages for now. More golang to come.