make() vs new() 在 Go 中:2025年该如何选择?清晰指南

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

Make 还是 New?Go开发者的永恒问题



你写Go代码已经一周或一年了,但有时在选择make()或new()时还是会犹豫片刻?这很正常。在Stack Overflow上,“Why would I make() or new()?”这个问题获得了数万次浏览,即使在2025年,它依然具有现实意义。为什么?因为在这看似简单的选择背后,隐藏着Go内存管理哲学的根本差异。



让我们一劳永逸地弄清楚这个问题,这样你就不用再猜测了。我们将从基础讲起,涵盖实际案例,指出陷阱,并提供一个日常工作的检查清单。开始吧!



简短回答(给赶时间的人)



如果你需要为切片(slice)、映射(map)或通道(channel)分配内存——请使用make()。在所有其他情况下,使用new()或字面量&Type{}。但魔鬼,总是在细节中。



什么是new()以及它如何工作



new(T)函数是Go的内置函数,它为类型T分配内存,将其清零(零值),并返回指向该内存的指针:*T。



简单来说:new()给你一个空但有效的对象。你得到一个指针,但如果对象需要初始化内部结构,它本身还不能完全正常工作。



package main

import "fmt"

type Person struct { Name string Age int}

func main() { p := new(Person) fmt.Println(p) // 输出:&{ 0} fmt.Println(p.Name == "") // true}


这里,new(Person)为结构体分配了内存,将字段清零(空字符串和0),并返回了指针。一切正常。



什么是make()及其超能力



make()也是一个内置函数,但它只适用于三种类型:切片、映射和通道。为什么只有它们?因为这些类型具有需要初始化的内部结构(头部),而不仅仅是清零。



make()不返回指针。它返回所需类型的已初始化值(不是指针!)。



// 切片:长度5,容量10s := make([]int, 5, 10)fmt.Println(len(s), cap(s)) // 5 10

// 映射:准备好插入元素m := make(map[string]int)m["key"] = 42fmt.Println(m) // map[key:42]

// 通道:缓冲大小为3ch := make(chan int, 3)ch <- 1fmt.Println(<-ch) // 1


请注意:如果你尝试写m := new(map[string]int),你将得到一个指向nil映射的指针。向这样的映射中插入元素会导致恐慌(panic)。



为什么new()不适用于切片、映射和通道?



让我们以切片为例。Go中的切片是一个由三个字段组成的结构体:指向数组的指针、长度和容量。new([]int)会为该结构体分配内存并将其清零。指针将为nil,长度和容量为0。你将得到&[]——一个指向nil切片的指针。它不会在按索引赋值时崩溃吗?不,它会崩溃。



而make([]int, 3)做了三件事:

  • 在底层分配一个包含3个元素的实际数组(如果指定了容量,则可能更多)。
  • 初始化切片头部(pointer, length, capacity)。
  • 返回一个准备好使用的切片。


映射也是如此:make(map[string]int)创建一个哈希表并为桶分配内存。new(map[string]int)只提供一个nil映射。



2025年何时使用new()?



Go社区当前的实践是:new()很少使用。大多数情况下,开发者更喜欢使用带取地址的字面量:&Type{}。这更具可读性,并且允许立即设置字段。

博客

书籍推荐