len函数只能用于数组、切片、字符串、map和channel;对nil切片或nil map调用合法且返回0;字符串len返回UTF-8字节数,非Unicode字符数;[]rune可获取真实字符数但有性能开销。
len 是 Go 内置函数,不是方法,只能用于编译期已知长度的类型:数组([5]int)、切片([]string)、字符串(string)、map(map[string]int)、channel(chan int)。它不能用于结构体、指针、函数或自定义类型(除非底层类型是上述之一)。
常见错误是试图对 nil 切片或 nil map 调用 len —— 实际上这完全合法:len(nil切片) 返回 0,len(nil map) 也返回 0。真正 panic 的是取值(如 m["k"])或遍历(for range nil map)。
len("你好") 返回 6(字节数,UTF-8 编码)len([]rune("你好")) 返回 2(Unicode 码点数)len([3]int{1,2,3}) 返回 3(数组长度固定)len(map[int]string{}) 返回 0(空 map)Go 字符串底层是只读字节序列,len(s) 永远返回 UTF-8 编码后的字节数。中文、emoji 等多字节字符会拉高这个值,但不反映“人眼看到的字符个数”。
如果业务需要按 Unicode 字符计数(比如截断显示、校验用户名最大 10 个汉字),必须转成 []rune:
name := "Hello世界?" fmt.Println(len(name)) // 输出: 13(H-e-l-l-o-世-界-? 各自 UTF-8 字节数之和) fmt.Println(len([]rune(name))) // 输出: 9(5 ASCII + 2 汉字 + 1 emoji = 9 个 rune)
注意:[]rune(s) 会分配新底层数组,对超长字符串(如 MB 级日志)频繁调用有性能开销。
数组长度是类型的一部分,len 在编译期确定;切片长度是运行时属性,可变。
arr := [4]int{1,2,3,4}; len(arr) → 永远是 4,不可修改sl := []int{1,2,3}; sl = sl[:2]; len(sl) → 变为 2len(sl) 和 cap(sl) 可能不同:切片可能只用了底层数组一部分误把 cap 当 len 用会导致逻辑错误,比如循环写满整个容量而非当前长度:
data := make([]byte, 0, 1024)
data = append(data, 'a', 'b')
// 错误:for i := 0; i < cap(data); i++ { ... } —— 会访问未初始化内存
// 正确:for i := 0; i < len(data); i++ { ... }
len(m) 返回当前键值对数量,len(ch) 返回当前缓冲区中待读取元素个数。两者都常用于条件判断,但要注意并发安全。
len 不是原子操作,多 goroutine 写入时,即使只读 len 也可能触发 panic(Go 1.19+ 对并发读写 map 的 panic 更敏感)len(ch) 是瞬时快照,无法替代 select 或 range 做同步控制len(ch) == cap(ch) 判断是否满 —— 非缓冲 channel 的 cap 是 0,len 也是 0,永远相等真正需要长度感知的并发场景,应配合 sync.Map、互斥锁或 channel 自身的阻塞语义来设计,而不是依赖 len 做轮询

最容易被忽略的是:字符串的 len 和 []rune 的 len 在语义上完全不同,而 Go 不提供类似 Python 的 len(s, encoding='utf8') 这种带选项的接口 —— 开发者必须自己决定用字节还是 rune,并承担转换成本。