Go面试重在真实编码经验,核心矛盾包括值/引用、栈/堆、并发安全/性能、接口隐式实现;make与new用途严格区分,defer对命名返回值才生效,map并发写必panic而slice可能静默损坏,nil接口因(type,data)二元组差异不等。

Go 语言面试题不是考背诵,而是看你在真实写代码时是否踩过坑、是否理解设计取舍。高频题集中在几个关键矛盾点:值 vs 引用、栈 vs 堆、并发安全 vs 性能、接口隐式实现带来的灵活性与隐蔽性。
为什么 make 和 new 不能混用?
因为它们干的是完全不同的事:new 只分配内存并置零,返回指针;make 只用于 slice/map/chan,它不仅分配内存,还初始化内部结构(比如 slice 的底层数组、map 的哈希桶)。
-
new([]int)返回*[]int,但这个切片是 nil,不能直接append—— 会 panic -
make([]int, 0)返回可用的空切片,底层数组已就位 - 对
struct只能用new或字面量,make编译报错:cannot make type struct{...} - 新手常错:看到“初始化”就下意识用
make,结果对*int或func调用make直接编译失败
为什么 defer 修改命名返回值有时生效、有时不生效?
关键在函数签名是否用了命名返回参数。只有命名返回值(如 func() (err error))才能被 defer 中的闭包修改;匿名返回值(如 func() error)不行。
-
defer函数体里改err = fmt.Errorf("oops"),对命名返回值有效,最终函数真返回这个新错误 - 但如果函数是
func() error { return fmt.Errorf("ok") },defer里的赋值只是改了局部变量,不影响返回值 - 更隐蔽的坑:
defer参数在声明时求值,不是执行时 ——i := 1; defer fmt.Println(i); i++输出的是1,不是2
为什么 map 并发读写会 crash,而 slice 不一定?
map 是 Go 运行时强保护的类型,一旦检测到并发写(哪怕只是两个 goroutine 同时 map[key] = val),直接 panic:fatal error: concurrent map writes;slice 没这层检查,但并发读写仍导致数据竞争 —— 表现为随机丢数据、越界或静默损坏,-race 能捕获,但线上可能长期潜伏。
- 修复
map并发:用sync.RWMutex包裹读写,或改用sync.Map(仅适合读多写少场景) - 别信“我只读不写所以 slice 安全”——如果另一个 goroutine 正在
append,底层数组可能被扩容复制,旧引用就指向失效内存 - 真正安全的共享数据结构,要么加锁,要么用 channel 传递所有权,而不是裸露引用
为什么 interface{} 能存任何值,但两个 nil 接口却不相等?
因为空接口底层是 (type, data) 二元组。nil 指针赋给 interface{} 后,type 非空(比如 *os.File),data 是 nil;而未初始化的 var x interface{} 是 type=nil 且 data=nil。两者底层不同,== 比较时直接判 false。
立即学习“go语言免费学习笔记(深入)”;
- 常见误判:
if err == nil安全,但if myErr == nil(其中myErr是interface{}类型)可能永远不成立 - 判断接口是否为“零值”,要用
if myErr == nil前先确认它是不是真的没装东西,否则应转成具体类型再判空 - 这个细节在写通用错误包装器或中间件时极易翻车,尤其是日志打印或上下文透传场景


















