强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Go 语言完全指南 / 03 - Hello World:项目结构、go run/build/install

03 - Hello World

3.1 最简单的 Go 程序

package main

import "fmt"

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

逐行解析:

代码说明
package main声明包名。main 包是可执行程序的入口
import "fmt"导入标准库的 fmt 包(格式化 I/O)
func main()程序入口函数。main 包必须有 main() 函数
fmt.Println()打印一行文本并换行

⚠️ 注意

  • 每个 .go 文件必须以 package 声明开头
  • 可执行程序必须是 package main,且有 func main()
  • import 后未使用的包会导致编译错误

3.2 标准项目结构

小型项目

myproject/
├── go.mod
├── go.sum
├── main.go
└── README.md

中型项目

myproject/
├── go.mod
├── go.sum
├── main.go
├── cmd/
│   └── server/
│       └── main.go
├── internal/
│   ├── handler/
│   │   ├── user.go
│   │   └── user_test.go
│   ├── model/
│   │   └── user.go
│   ├── service/
│   │   └── user.go
│   └── repository/
│       └── user.go
├── pkg/
│   └── utils/
│       └── helper.go
├── config/
│   └── config.go
├── migrations/
│   └── 001_init.sql
├── docs/
│   └── api.md
├── Makefile
├── Dockerfile
└── .gitignore

Go 特有的目录约定

目录说明
cmd/可执行程序入口
internal/私有代码,不可被外部包导入
pkg/可被外部导入的公共库
vendor/依赖的本地副本(go mod vendor)
testdata/测试数据,go test 会忽略
api/API 定义文件(protobuf、OpenAPI)
build/打包和 CI 配置
scripts/构建、安装、分析脚本

3.3 go run —— 编译并运行

# 基本用法
go run main.go

# 运行多个文件
go run main.go helper.go

# 运行当前目录下所有文件
go run .

# 传递参数
go run . --name=World

# 传递编译器标志
go run -race .          # 开启竞态检测
go run -gcflags="-m" .  # 查看逃逸分析
go run -ldflags="-s -w" . # 减小二进制体积

go run 的工作流程:

源代码 → 编译到临时目录 → 执行 → 删除临时文件

💡 技巧go run 适合开发调试,但不生成可执行文件。

3.4 go build —— 编译

# 编译当前包
go build

# 编译并指定输出文件名
go build -o myapp

# 编译指定文件
go build -o myapp main.go

# 编译指定包路径
go build -o myapp ./cmd/server/

# 查看编译过程
go build -v .

# 编译并打印汇编
go build -gcflags="-S" . 2>&1 | head -50

交叉编译

# 查看支持的平台
go tool dist list

# 编译 Linux 64位
GOOS=linux GOARCH=amd64 go build -o myapp-linux .

# 编译 Windows 64位
GOOS=windows GOARCH=amd64 go build -o myapp.exe .

# 编译 macOS ARM (M1/M2/M3)
GOOS=darwin GOARCH=arm64 go build -o myapp-mac .

# 编译 Linux ARM(树莓派等)
GOOS=linux GOARCH=arm GOARM=7 go build -o myapp-arm .

# 编译为 WebAssembly
GOOS=js GOARCH=wasm go build -o main.wasm .

常用交叉编译组合:

GOOSGOARCH用途
linuxamd64Linux 服务器
linuxarm64ARM 服务器 / Docker
darwinamd64Intel Mac
darwinarm64Apple Silicon Mac
windowsamd64Windows 桌面
jswasmWebAssembly

编译优化

# 去掉调试信息,减小体积
go build -ldflags="-s -w" -o small-app .

# 启用竞态检测(开发/测试时使用)
go build -race -o app-race .

# 静态编译(不依赖动态链接库)
CGO_ENABLED=0 go build -o static-app .

# 查看二进制大小分析
go build -o app .
go tool nm app | wc -l       # 符号数量
ls -lh app                   # 文件大小

3.5 go install —— 安装

# 安装到 $GOPATH/bin
go install

# 安装远程工具
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest

# 安装特定版本
go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.55.0

go build vs go install vs go run 对比:

特性go rungo buildgo install
编译
执行
输出位置临时目录当前目录$GOPATH/bin
缓存不缓存二进制缓存编译结果缓存编译结果
用途开发调试构建二进制安装工具

3.6 go fmt —— 格式化

# 格式化当前包
go fmt

# 格式化指定包
go fmt ./...

# 检查格式但不修改(dry run)
gofmt -d .

# 使用 goimports(更强大,自动管理 import)
goimports -w .

💡 技巧:Go 的格式化是确定性的,所有 Go 代码用 go fmt 格式化后看起来一样。这是 Go 社区的共识——不再争论代码风格。

3.7 其他常用命令

# 查看包文档
go doc fmt.Println
go doc -all fmt

# 静态分析
go vet ./...

# 清理构建缓存
go clean

# 查看环境变量
go env GOPATH
go env GOPROXY

# 下载依赖
go mod download

# 列出可用的更新
go list -m -u all

# 生成 vendor 目录
go mod vendor

# 查看依赖树
go mod graph | head -20

3.8 完整示例:多文件项目

main.go

package main

import (
    "fmt"
    "os"

    "example.com/greet/greet"
)

func main() {
    name := "World"
    if len(os.Args) > 1 {
        name = os.Args[1]
    }

    message := greet.Hello(name)
    fmt.Println(message)
    fmt.Println(greet.Goodbye(name))
}

greet/greet.go

package greet

import "fmt"

// Hello returns a greeting message.
func Hello(name string) string {
    return fmt.Sprintf("你好, %s! 欢迎学习 Go!", name)
}

// Goodbye returns a farewell message.
func Goodbye(name string) string {
    return fmt.Sprintf("再见, %s!", name)
}

greet/greet_test.go

package greet

import "testing"

func TestHello(t *testing.T) {
    got := Hello("Go")
    want := "你好, Go! 欢迎学习 Go!"
    if got != want {
        t.Errorf("Hello(\"Go\") = %q, want %q", got, want)
    }
}

func TestGoodbye(t *testing.T) {
    got := Goodbye("Go")
    want := "再见, Go!"
    if got != want {
        t.Errorf("Goodbye(\"Go\") = %q, want %q", got, want)
    }
}
# 运行测试
go test ./greet/ -v

# 运行整个项目
go run . "Go开发者"

3.9 Makefile 最佳实践

APP_NAME := myapp
VERSION := 1.0.0
BUILD_TIME := $(shell date -u '+%Y-%m-%d_%H:%M:%S')
GO_VERSION := $(shell go version | awk '{print $$3}')

.PHONY: all build run test clean lint

all: lint test build

build:
	@echo "Building $(APP_NAME)..."
	go build -ldflags="-s -w -X main.version=$(VERSION)" -o bin/$(APP_NAME) .

run:
	go run .

test:
	go test -v -race -cover ./...

lint:
	golangci-lint run ./...

clean:
	rm -rf bin/

docker:
	docker build -t $(APP_NAME):$(VERSION) .

# 查看构建信息
version:
	@echo "App: $(APP_NAME)"
	@echo "Version: $(VERSION)"
	@echo "Go: $(GO_VERSION)"
	@echo "Build Time: $(BUILD_TIME)"

🏢 业务场景

  1. 快速原型go run . 快速验证想法
  2. CI/CD 流水线go build -ldflags 注入版本号和构建时间
  3. 多平台发布:交叉编译 Linux/Windows/macOS 二进制
  4. Docker 部署:静态编译后放入 scratch 镜像

📖 扩展阅读