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 .
常用交叉编译组合:
| GOOS | GOARCH | 用途 |
|---|---|---|
| linux | amd64 | Linux 服务器 |
| linux | arm64 | ARM 服务器 / Docker |
| darwin | amd64 | Intel Mac |
| darwin | arm64 | Apple Silicon Mac |
| windows | amd64 | Windows 桌面 |
| js | wasm | WebAssembly |
编译优化
# 去掉调试信息,减小体积
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/[email protected]
go build vs go install vs go run 对比:
| 特性 | go run | go build | go 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)"
🏢 业务场景
- 快速原型:
go run .快速验证想法 - CI/CD 流水线:
go build -ldflags注入版本号和构建时间 - 多平台发布:交叉编译 Linux/Windows/macOS 二进制
- Docker 部署:静态编译后放入 scratch 镜像