I'm just discovering Go. When I started looking at it last week, I found out GOPATH and how Go apparently is very opinionated about what directories you store your code in. So I cursed Google's name and decided Go wasn't for me, then recently heard about Go modules and how apparently they resolve this very issue.
The trouble is that information online on how to structure a module-based Go project appears to be very scarce. I'm having trouble figuring out how to lay out my code and what to call the packages in order to get imports working. I've tried various things and looked at examples but no matter what I do, I get "unknown import path" errors.
Basically I want to have a directory with, say, main.go
and library.go
, maybe with library.go
in a subdirectory. I want to be able to write import library
or something similar in main.go
and be able to access members of library.go
. I also want to be able to run the code by typing go run main.go
. Is this achievable?
It is confusing and the documentation assumes a level of familiarity with Golang. I've been trying to understand Modules recently. I hope the following helps.
Best wishes learning Golang.
WORKDIR=[[PATH-TO-YOUR-WORKING-DIRECTORY]]
mkdir -p ${WORKDIR}/go
export GOPATH=${WORKDIR}/go
export PATH=${GOPATH}/bin:${PATH}
mkdir -p {${WORKDIR}/go/src/foo, ${WORKDIR}/go/src/foo/bar}
Then create ${WORKDIR}/go/src/foo/bar/library.go
:
package bar
func Something() (string) {
return "Hello Freddie"
}
Then create ${WORKDIR}/go/src/foo/main.go
:
package main
import (
"fmt"
"foo/bar"
)
func main() {
fmt.Printf("%s", bar.Something())
}
You'll have a structure like this:
.
└── go
└── src
└── foo
├── bar
│ └── library.go
└── main.go
Then you may run this either of the following ways:
GO111MODULE=off go run ${WORKDIR}/go/src/main.go
Hello Freddie!
cd ${WORKDIR}/go/src/
GO111MODULE=off go run main.go
Hello Freddie!
GO111MODULE=off go run foo
Hello Freddie!
Assuming you've done the above!
You don't need to do this step but this is the new best practice. We're moving our sources outside of ${GOPATH}
. ${GOPATH}
is still used to store our versioned packages. NB In this trivial example, we're not using external packages so ${GOPATH}
remains empty.
mv ${WORKDIR}/go/src/foo ${WORKDIR}
rm ${WORKDIR}/go/src
You should a structure like this:
.
├── foo
│ ├── bar
│ │ └── library.go
│ └── main.go
└── go
NB Our sources are now outside of ${GOPATH}
cd ${WORKDIR}/foo
GO111MODULE=on go mod init foo
more go.mod
module foo
go 1.12
GO111MODULE=on go run foo
Hello Freddie!
GO111MODULE=on go run main.go
Hello Freddie!
To show the difference with external packages:
No Modules pulls the latest version of the package into ${GOPATH}/src
:
GO111MODULE=off go get github.com/golang/glog
.
├── foo
│ ├── bar
│ │ └── library.go
│ ├── go.mod
│ ├── go.sum
│ └── main.go
└── go
└── src
└── github.com
└── golang
└── glog
├── glog_file.go
├── glog.go
├── glog_test.go
├── LICENSE
└── README
Versus using Modules, pulls a specific version (or versions) of the package into ${GOPATH}/pkg
:
GO111MODULE=on go get github.com/golang/glog
.
├── foo
│ ├── bar
│ │ └── library.go
│ ├── go.mod
│ ├── go.sum
│ └── main.go
└── go
└── pkg
├── linux_amd64
│ └── github.com
│ └── golang
│ └── glog.a
└── mod
├── cache
│ ├── download
│ │ └── github.com
│ │ └── golang
│ │ └── glog
└── github.com
└── golang
└── glog@v0.0.0-20160126235308-23def4e6c14b
├── glog_file.go
├── glog.go
├── glog_test.go
├── LICENSE
└── README
library.go
so I've used that and conventionally, files are named after their content.library.go
could have been called freddie.go
or something.go
, it's important that it be package bar
and in a directory called bar
.main.go
is conventionally where func main() {...}
is defined and func main() {...}
must be in a package called main
. So, even though the directory is called foo
, because we want a main
function, we name the file main.go
and it must be package main
.bar
, it's important that Something
be initially capitalized (S
). This exports the function from the bar
package so that it may be used by other packages. If the function's name were lowercase (something
), the function is only available to other functions within the bar
package.