Go程序必须有且仅有一个package main中的func main(),否则编译失败;:=仅限函数内使用;错误必须显式处理;优先用切片和map而非数组;结构体方法接收者类型决定是否可修改原值。

main 函数必须存在,且只能在 main 包中 —— 这是 Go 程序能跑起来的硬门槛。没它,编译直接失败;包名写错(比如写成 package Main 或 package main1),照样报错:`cannot find package "main"` 或 `package main is not a main package`。
为什么必须这样? Go 不靠文件名或入口标记识别程序,而是靠包名 + 函数名双重约定。只有 package main 里定义的 func main() 才会被构建为可执行文件。其他包哪怕有 main 函数,也只是普通函数。
常见踩坑点:
- 新建文件时复制了旧项目的包声明,忘了改成
package main - 把
main.go放进子目录(如cmd/app/main.go),但没确保该目录下所有 .go 文件都属main包(否则编译器会忽略) - 误用
init()替代main()——init不能启动程序,只用于初始化
变量声明别用 var 硬写,优先用 :=,但得知道它在哪能用
:= 是短变量声明,自动推类型、自动初始化,写起来快,读起来也干净。但它**只能在函数内部使用**。一到包级作用域(也就是函数外),就只能用 var。
错误示范:
name := "Alice" // 编译报错:syntax error: non-declaration statement outside function body
func main() {
fmt.Println(name)
}
正确写法:
var name = "Alice" // 包级变量,允许
func main() {
age := 25 // 函数内,用 :=
fmt.Println(name, age)
}
更关键的是:Go **禁止声明但不使用变量**。所以别写:
var unused string // 编译失败:declared but not used这倒逼你保持代码精简——不是限制,是设计约束。
错误处理不是可选项,而是调用函数后的必填动作
Go 没有try/catch,错误是值,不是异常。几乎所有 I/O、解析、网络操作都返回 (result, error),而 error 必须显式检查,否则程序可能静默失败。
典型模式:
data, err := os.ReadFile("config.json")
if err != nil {
log.Fatal(err) // 或 return err,取决于上下文
}
// 后续逻辑才安全使用 data
容易错的地方:
- 写成
if err == nil { ... }—— 习惯上先处理错误分支,让正常流程更“扁平”,避免深层嵌套 - 忽略
err:比如json.Unmarshal(data, &v)后不检查 err,导致结构体字段为空却无提示 - 用
panic替代错误返回 —— 仅适用于真正不可恢复的编程错误(如索引越界),绝不该用于文件不存在、HTTP 404 这类业务错误
别碰数组,直接学切片 []T 和映射 map[K]V
Go 的数组([3]int)长度固定、值语义(赋值即拷贝),几乎不用。日常要的是动态、灵活、引用语义的 []int(切片)和 map[string]int(映射)。
切片要点:
- 字面量直接写
[]string{"a", "b"},不用new或make - 扩容用
append,它返回新切片,原切片不受影响:s2 := append(s1, "x") - 切片表达式
s[i:j]不检查越界,运行时 panic;但s[i:]或s[:j]允许超出len(只要 ≤cap)
映射要点:
- 必须
make初始化,否则是nil,往里写会 panic:m := make(map[string]int) - 读取不存在的 key 返回零值(
0,"",false),不报错;想区分“零值”和“未设置”,得用双返回值:v, ok := m["key"]
结构体 + 方法才是 Go 的“类”替代方案,但接收者类型决定能否改原值 —— 想修改,必须用 *T;只读,用 T。这点一旦写反,调试半天才发现字段没变。










