我应该如何构造Go模块,以便能够轻松运行它并导入内部软件包?

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.

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.