根本原因是将包名list误作类型,正确参数类型只能是list.List;因list.List含指针字段,值传递会导致操作失效;遍历时需用list.Element,其Value为interface{},须断言且判空,Remove等操作前必须检查nil。

函数参数必须是 *list.List,不是 list 或 list.List
编译报错 undefined: list 的根本原因,是你把包名当成了类型。Go 里 container/list 是包路径,导出的链表类型是 *list.List(指针),不是 list,也不是 list.List 字面量。写成 func f(l list) 或 func f(l list.List) 都会失败。
正确做法只有一种:func f(l *list.List)。传指针不只是习惯——list.List 内部含指针字段(如 root *Element),值传递会复制整个结构但不复制其指向的节点,导致后续 PushFront 等操作失效。
-
l := list.New()返回的就是*list.List,直接用,别加& -
var l list.List是合法零值,但容易让人误以为要传&l;推荐统一用list.New() - 函数返回新链表时也必须返回
*list.List,不能返回list.List值类型
遍历时操作的是 *list.Element,不是 *list.List
*list.List 没有 .Value、.Next()、.Prev() 这些方法。这些属于 *list.Element。常见错误是写 l.Value 或 l.Next(),编译直接报错:unknown field or method。
遍历必须从 l.Front() 或 l.Back() 开始,拿到 *list.Element 后再用 e.Value 和 e.Next():
-
e := l.Front()返回*list.Element;空链表时返回nil,必须判空再访问e.Value -
e.Value类型永远是interface{},存整数就得写e.Value.(int);存结构体指针就得写e.Value.(*MyStruct) - 循环中更新的是
e = e.Next(),不是l = l.Next()(*list.List根本没Next方法)
Remove、InsertBefore 等操作前必须检查 Element 是否为 nil
l.Remove(e)、l.InsertBefore(x, e) 这类函数内部不检查 e == nil,直接解引用就会 panic:invalid memory address or nil pointer dereference。
典型翻车场景:连续调两次 l.Remove(l.Front()),第二次 l.Front() 返回 nil,传给 Remove 就崩。
- 安全写法永远是:
if e != nil { l.Remove(e) } - 更稳妥的做法是先判断长度:
if l.Len() > 0 { e := l.Front(); l.Remove(e) } -
l.Front()和l.Back()在空链表时都返回nil,不是特殊哨兵节点
Value 类型断言必须匹配存储时的实际类型
存进去是什么,取出来就得断言成什么。存结构体指针 &Player{},就必须用 e.Value.(*Player);存结构体值 Player{},才用 e.Value.(Player)。断言错类型会 panic:interface conversion: interface {} is *main.Player, not main.Player。
如果不确定类型,用双值断言避免 panic:
-
v, ok := e.Value.(int)—— ok 为 false 时 v 是零值,不会崩溃 - 对结构体指针也一样:
p, ok := e.Value.(*Player) - 不要在生产代码里裸写
e.Value.(int),除非你 100% 控制输入来源
最易被忽略的点:*list.Element 一旦被 Remove,它的 Next() 和 Prev() 就失效了,不能再用。遍历时若中途删节点,必须提前保存下一个元素指针,否则迭代会断。

















