1. Go的下载、安装与配置

1.1 Go的下载

根据自己计算机的芯片类型和操作系统,从Go语言官方网站 https://golang.google.cn/dl/ 选择相应的稳定版(Stable versions)的安装包进行下载。以Go v1.22.3在macOS(arm64/Apple Silicon)为例,可在终端(Terminal)中执行以下命令行进行下载,并使用sha256sum命令进行下载文件完整性校验:

  mkdir -p /opt/go
  cd /opt/go
  wget -c "https://golang.google.cn/dl/go1.22.3.darwin-arm64.tar.gz" -O go1.22.3.darwin-arm64.tar.gz
  echo "02abeab3f4b8981232237ebd88f0a9bad933bc9621791cd7720a9ca29eacbe9d *go1.22.3.darwin-arm64.tar.gz" | sha256sum -c -

1.2 Go的安装与环境配置

以将Go安装到/opt/go/1.22.3/下为例进行说明,可在终端(Terminal)中执行以下命令:

  tar -zxvf go1.22.3.darwin-arm64.tar.gz
  mv go 1.22.3
  rm -f go1.22.3.darwin-arm64.tar.gz

配置Go相关的环境变量,可将以下SHELL代码片段添加到.bashrc或.profile中,推荐保存到/etc/profile.d/go.sh中:

if [ -z "${GOROOT}" ] ; then
    go_version="1.22.3"
    go_root="/opt/go/${go_version}"

    if [ -x "${go_root}/bin/go" ] ; then
        GOROOT="${go_root}" ; export GOROOT
        
        GOPATH="/data/repo/go" ; export GOPATH

        GOPROXY="https://mirrors.aliyun.com/goproxy/,direct" ; export GOPROXY
        # GO111MODULE="on" ; export GO111MODULE

        PATH=${GOROOT}/bin:${GOPATH}/bin:${PATH} ; export PATH
    fi

    unset go_root
    unset go_version
fi

其中,GOPROXY环境变量使用Go模块包阿里云国内镜像地址进行设置以进行加速,Go模块安装到/data/repo/go下(请先创建该目录)。

1.3 安装Go的gopls、delve及staticcheck

  • gopls: 读音:“Go please”,是Go官方开发团队开发的Go语言服务器,为兼容LSP的编辑器如Visual Studio Code提供Go语言IDE相关功能;
  • delve:Go语言的调试器;
  • staticcheck: Go语言的静态分析器(static analysis),用于分析查找缺陷(Bug)和性能问题给出提示,强化编程规范。

可在终端(Terminal)中执行以下命令进行安装:

  go install golang.org/x/tools/gopls@latest
  go install github.com/go-delve/delve/cmd/dlv@latest
  go install -v honnef.co/go/tools/cmd/staticcheck@latest

2. Visual Studio Code软件及Go相关插件的下载、安装与配置

根据自己计算机的芯片类型和操作系统,从Visual Studio Code官方网站 https://code.visualstudio.com/Download 下载安装Visual Studio Code,安装后在Visual Studio Code插件扩展市场搜索“Go”,找到“Go-Rich Go language support for Visual Studio Code”插件进行安装。可在终端(Terminal)中执行以下命令进行安装Visual Studio Code,供参考:

  wget -c "https://code.visualstudio.com/sha/download?build=stable&os=darwin-arm64" -O VSCode-darwin-arm64.zip
  echo "1ab1a3776be133c9e9cf7e516d9a6470adb5a89d39e37357f6a7cc81cd0a0a3e *VSCode-darwin-arm64.zip" | sha256sum -c -
  unzip VSCode-darwin-arm64.zip
  mv Visual\ Studio\ Code.app/ /Applications/

3. Go语言的开发步骤简介

以下在/data/project/go下创建demo项目,简要说明一个Go语言项目开发步骤,可在终端(Terminal)中执行以下命令:

  mkdir -p /data/project/go/demo/greetings
  mkdir -p /data/project/go/demo/hello
  cd /data/project/go/demo/greetings
  go mod init aihua.gitlab.com/greetings
  cd /data/project/go/demo/hello
  go mod init aihua.github.com/hello
  go mod edit -replace "aihua.gitlab.com/greetings=../greetings"
  go mod tidy

项目初始化后,可在Visual Studio Code中打开/data/project/go/demo文件夹,新建Go文件编写相关代码及测试代码,单元测试代码以“_test.go”作文件名后缀。

Go代码编写完成后,可使用Visual Studio Code的Run和Debug进行运行和调试。也可可在终端(Terminal)中执行以下命令进行运行与测试:

  go test
  go test -v
  go test -cover
  go test -benchmem
  go run .

demo项目的说明:

项目的目录结构如下:

.
├── greetings
│   ├── go.mod
│   ├── greetings.go
│   └── greetings_test.go
└── hello
    ├── go.mod
    └── hello.go

aihua.gitlab.com/greetings模块未发布时,需要使用go mod edit -replace "aihua.gitlab.com/greetings=../greetings"go mod tidy命令来处理模块的依赖关系。以下是主要的代码,其中demo/hello/hello.go为主调方法入口。

demo/hello/hello.go代码:

package main

import (
    "fmt"
    "log"

    "aihua.gitlab.com/greetings"
)

func main() {
    // Set properties of the predefined Logger, including
    // the log entry prefix and a flag to disable printing
    // the time, source file, and line number.
    log.SetPrefix("greetings: ")
    log.SetFlags(0)

    // A slice of names.
    names := []string{"Gladys", "Samantha", "Darrin"}

    // Request greeting messages for the names.
    messages, err := greetings.Hellos(names)
    if err != nil {
        log.Fatal(err)
    }
    // If no error was returned, print the returned map of
    // messages to the console.
    fmt.Println(messages)
}

demo/greetings/greetings.go代码:

package greetings

import (
    "errors"
    "fmt"
    "math/rand"
)

// Hello returns a greeting for the named person.
func Hello(name string) (string, error) {
    // If no name was given, return an error with a message.
    if name == "" {
        return name, errors.New("empty name")
    }
    // Create a message using a random format.
    message := fmt.Sprintf(randomFormat(), name)
    return message, nil
}

// Hellos returns a map that associates each of the named people
// with a greeting message.
func Hellos(names []string) (map[string]string, error) {
    // A map to associate names with messages.
    messages := make(map[string]string)
    // Loop through the received slice of names, calling
    // the Hello function to get a message for each name.
    for _, name := range names {
        message, err := Hello(name)
        if err != nil {
            return nil, err
        }
        // In the map, associate the retrieved message with
        // the name.
        messages[name] = message
    }
    return messages, nil
}

// randomFormat returns one of a set of greeting messages. The returned
// message is selected at random.
func randomFormat() string {
    // A slice of message formats.
    formats := []string{
        "Hi, %v. Welcome!",
        "Great to see you, %v!",
        "Hail, %v! Well met!",
    }

    // Return one of the message formats selected at random.
    return formats[rand.Intn(len(formats))]
}

demo/greetings/greetings_test.go代码:

package greetings

import (
    "testing"
    "regexp"
)

// TestHelloName calls greetings.Hello with a name, checking
// for a valid return value.
func TestHelloName(t *testing.T) {
    name := "Gladys"
    want := regexp.MustCompile(`\b`+name+`\b`)
    msg, err := Hello("Gladys")
    if !want.MatchString(msg) || err != nil {
        t.Fatalf(`Hello("Gladys") = %q, %v, want match for %#q, nil`, msg, err, want)
    }
}

// TestHelloEmpty calls greetings.Hello with an empty string,
// checking for an error.
func TestHelloEmpty(t *testing.T) {
    msg, err := Hello("")
    if msg != "" || err == nil {
        t.Fatalf(`Hello("") = %q, %v, want "", error`, msg, err)
    }
}