Go简单入门

210 阅读5分钟

这篇文章主要用于说明Go环境搭建,且以hello world为例进行具体说明。

环境

系统: CentOS 7.7

Go版本: go1.16.6

安装

下载并解压

wget https://golang.google.cn/dl/go1.16.6.linux-amd64.tar.gz

tar -C /usr/local -xzf go1.16.6.linux-amd64.tar.gz

配置

我比较习惯在/etc/profile.d目录创建专用的shell文件,比如go.sh用于保存GO相关的环境变量

export PATH=$PATH:/usr/local/go/bin
export GOPATH=/usr/work/

GOPATH通常是GO项目的路径,在GO11之前会经常用到。

GO同样需要设置代理来加速依赖下载,根据go的版本不同设置方式有区别,当前go1.16可以采用下面方式:

go env -w GOPROXY=https://goproxy.cn,direct

从Go1.11开始,Go允许在$GOPATH/src之外的任何目录通过go mod创建项目,而go module就是go 11引入的新特性,它主要有三个属性值:

  • GO111MODULE=off: 无模块支持,go会从GOPATH和vendor文件寻找包
  • GO111MODULE=on: 模块支持,go会忽略GOPATH和vendor文件夹,只根据go.mod下载依赖
  • GO111MODULE=auto:在项目根目录存在go.mod文件,开启模块支持

设置模块支持

go env -w GO111MODULE=on

简单示例

hello world

创建项目

mkdir hello
cd hello

初始化项目

go mod init example/hello

创建代码文件hello.go

package main

import "fmt"

func main() {
    fmt.Println("Hello, World!")
}

运行代码

$ go run .
Hello, World!

引入依赖包

上面的hello world,将项目的初始化、构建、运行的流程做了简单描述,但是还没有用到依赖包。这里按照go官方文档说明,引入依赖包rsc.io/quote

在上面的hello.go文件中进行改动

package main

import "fmt"

import "rsc.io/quote"

func main() {
    fmt.Println(quote.Go())
}

由于引入了新的包,需要执行

go mod tidy

执行完之后,go.mod和go.sum文件会发生变化

运行代码

go run .

vscode debug调试

如果需要vscode debug调试go程序,需要安装go插件,同时需要安装golang.org/x/tools/gopls

自定义Module

创建Module

创建项目目录greetings

mkdir greetings
cd greetings

初始化项目

go mod init example.com/greetings

代码文件greetings.go

package greetings

import "fmt"

// Hello returns a greeting for the named person.
func Hello(name string) string {
	// Return a greeting that embeds the name in a message.
	message := fmt.Sprintf("Hi, %v. Welcome!", name)
	return message
}

引用Module

创建项目hello,并初始化,在hello.go文件中

package main

import (
	"fmt"

	"example.com/greetings"
)

func main() {
	// Get a greeting message and print it.
	message := greetings.Hello("Gladys")
	fmt.Println(message)
}

由于当前example.com/greetings没有发布,需要修改example.com/hello以便获取greetings的位置

go mod edit -replace example.com/greetings=../greetings

同步example.com/hello模块依赖

go mod tidy

go.mod中会为example.com/greetings预设版本号,此时可以修改为自定义的版本号

require example.com/greetings v1.0.0

运行项目

go run .

复杂一些

处理Error

处理错误是代码健壮性的一个重要体现。

greetings/greetings.go中增加错误处理逻辑

package greetings

import (
    "errors"
    "fmt"
)

// 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 "", errors.New("empty name")
    }

    // If a name was received, return a value that embeds the name
    // in a greeting message.
    message := fmt.Sprintf("Hi, %v. Welcome!", name)
    return message, nil
}

修改hello/hello.go

package main

import (
    "fmt"
    "log"

    "example.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)

    // Request a greeting message.
    message, err := greetings.Hello("")
    // If an error was returned, print it to the console and
    // exit the program.
    if err != nil {
        log.Fatal(err)
    }

    // If no error was returned, print the returned message
    // to the console.
    fmt.Println(message)
}

运行项目

go run .

返回随机问候

修改greetings/greetings.go

package greetings

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

// 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
}

// init sets initial values for variables used in the function.
func init() {
    rand.Seed(time.Now().UnixNano())
}

// 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 a randomly selected message format by specifying
    // a random index for the slice of formats.
    return formats[rand.Intn(len(formats))]
}

修改hello/hello.go

package main

import (
    "fmt"
    "log"

    "example.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)

    // Request a greeting message.
    message, err := greetings.Hello("Gladys")
    // If an error was returned, print it to the console and
    // exit the program.
    if err != nil {
        log.Fatal(err)
    }

    // If no error was returned, print the returned message
    // to the console.
    fmt.Println(message)
}

运行项目

go run .

为多个人返回问候

增加遍历逻辑,修改greetings/greetings.go

package greetings

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

// 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
}

// Init sets initial values for variables used in the function.
func init() {
	rand.Seed(time.Now().UnixNano())
}

// 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))]
}

修改hello/hello.go

package main

import (
	"fmt"
	"log"

	"example.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)
}

运行项目

go run .

代码测试

在greetings目录,创建测试文件greetings_test.go

package greetings

import (
	"regexp"
	"testing"
)

// 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)
	}
}

可以直接在vscode进行测试

image.png

或者是执行命令

go test -v

项目构建

在上面主要使用的命令是go run .运行项目,这里熟悉下其他的命令。

构建项目

go build
./hello

可以通过go list命令查看安装路径,执行之后发现在GOPATH

go list -f '{{ .Target }}'

也可以自定义安装路径

go env -w GOBIN=/usr/work/go

此时如果执行命令go install,则会在/usr/work/go看到生成的hello