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.
The previous|current way without Modules:
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!
The new (!) way with Modules:
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!
For Completeness
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
Notes
- Our file names are somewhat arbitrary. You said
library.go so I've used that and conventionally, files are named after their content.
- Our directory names are important. Go uses directory names to help find packages. So, even though
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.
- An exception to these rules is that
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.
- In package
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.