新手常犯的 Go 语法错误,一次性帮你避坑

新手常犯的 Go 语法错误,一次性帮你避坑


1. :== 搞混

最常见的第一坑。

go 复制代码
go
// ❌ 报错:no new variables on left side of :=
count = count + 1

// ✅ 正确
count += 1

规则 :函数体外部只能用 =,函数体内部第一次赋值用 :=,之后用 =


2. 声明了变量却不用

go 复制代码
go
// ❌ 编译报错:declared and not used
var name = "golang"

// ✅ 方案一:用掉它
fmt.Println(name)

// ✅ 方案二:用 _ 忽略
_ = name

Go 不允许有"死变量",这不是建议,是硬规则。


3. short var 声明时,左边必须有至少一个新变量

go 复制代码
go
// ❌ 报错:no new variables on left side of :=
x := 1
x, y := 2, 3   // x 已存在,不能用 :=

// ✅
x, y = 2, 3    // 用 = 重新赋值

4. 忘记 return 的命名返回值会返回零值

go 复制代码
go
func divide(a, b float64) (result float64, err error) {
    if b == 0 {
        return  // ❌ err 是 nil,result 是 0,调用方无法判断是真的返回了 0 还是出错了
    }
    return a / b, nil
}

// ✅
if b == 0 {
    return 0, errors.New("division by zero")
}

这是新手最容易踩的逻辑坑------不报错,但行为完全不对


5. 切片(slice)引用同一底层数组

less 复制代码
go
// ❌ 两个切片共享底层数组,改一个全变
a := []int{1, 2, 3}
b := a
b[0] = 99
fmt.Println(a)  // [99 2 3]

// ✅ 需要拷贝
b := make([]int, len(a))
copy(b, a)

// ✅ 或直接切(但注意容量)
b := a[:]  // 仍然共享!
b := a[:len(a):len(a)]  // 限制容量,append 时才不互相影响

6. for 循环变量捕获问题(Go 1.22 之前)

go 复制代码
go
// ❌ 输出的全是 3(Go 1.21 及以前)
for i := 0; i < 3; i++ {
    go func() {
        fmt.Println(i)  // 闭包捕获的是同一个 i
    }()
}

// ✅ 传参
for i := 0; i < 3; i++ {
    go func(v int) {
        fmt.Println(v)
    }(i)
}

Go 1.22 已修复此行为,循环变量在每次迭代中独立。但如果你还在用旧版本,这个坑必须知道。


7. nil slice 和空 slice 不一样

go 复制代码
go
var s1 []int           // nil slice
s2 := []int{}          // empty slice
s3 := make([]int, 0)   // empty slice

fmt.Println(s1 == nil)   // true
fmt.Println(s2 == nil)   // false
fmt.Println(len(s1))     // 0
fmt.Println(cap(s1))     // 0

// JSON 序列化时差异明显
json.Marshal(s1)  // null
json.Marshal(s2)  // []

建议 :返回空集合时,优先返回 nil,避免 JSON 输出不一致。


8. 结构体字段首字母小写 = 私有

go 复制代码
go
type User struct {
    Name string  // ✅ 导出(大写开头)
    age  int     // ❌ 私有,包外无法访问
}

// ✅ 命名规范:需要导出就大写,不需要就小写,别随心所欲

这不是"语法错误",但 90% 的新手在包间传递数据时撞墙,都是因为这个。


9. defer 的执行顺序搞反了

go 复制代码
go
func example() {
    defer fmt.Println("A")
    defer fmt.Println("B")
}
// 输出:B A(后进先出,像栈)

如果你写 defer file.Close() 之后又写了 defer file.Write(...),文件可能在还没写完时就关了。


10. select 里的 default 不是"随便执行"

go 复制代码
go
select {
case ch <- 1:
    fmt.Println("sent")
default:
    fmt.Println("nobody listening")
}

default 只在所有 case 都不可用时 立即执行。如果你想"等一等再决定",不要用 select,用 time.After


速查表

错误 现象 解决
:= 用在已有变量 编译报错 改用 =
变量声明不用 编译报错 _ 忽略或删掉
命名返回值忘赋值 逻辑错误 显式 return 零值+err
切片共享底层数组 数据被意外修改 copy() 或重新分配
for 闭包捕获 i 输出全是最后的值 传参或 Go 1.22+
nil vs empty slice JSON 输出不一致 统一用 nil
字段小写 包外访问不了 大写首字母导出

这些坑不复杂,但每个都够你调半小时。收藏这篇,写代码时对照着查,比事后 debug 快十倍。

相关推荐
神奇小汤圆2 小时前
学完 Spring Boot 再看 FastAPI,我破防了
后端
用户987409238872 小时前
deepspeed zero3 + llamafactory 保存checkpoint后第一step 就 OOM
后端
长大19882 小时前
ggplot2 高阶美化:SCI 期刊级论文图表从零绘制全流程
后端
墩墩大魔王丶3 小时前
macOS Rust 安装教程:自定义 CARGO_HOME 和 RUSTUP_HOME
后端
进阶的小名4 小时前
Spring Boot SSE + Nginx 配置:解决 EventSource 不实时返回、连接超时、流式响应被缓冲问题
spring boot·后端·nginx
摇滚侠5 小时前
SpringMVC 入门到实战 RESTFul 49-55
java·开发语言·后端·spring·intellij-idea·restful
PinkSun5 小时前
Spring AI RAG踩坑:我骂了半年的FilterExpression,其实是背锅侠
后端·ai编程
我登哥MVP5 小时前
SpringCloud Alibaba 核心组件解析:服务链路追踪
java·spring boot·后端·spring·spring cloud·java-ee·maven
by————组态5 小时前
Ricon组态系统 - 新一代Web可视化组态平台
前端·后端·物联网·架构·组态·组态软件
云技纵横5 小时前
ThreadLocal 内存泄漏:你的应用正在悄悄 OOM
后端