How to Create a Linked List in Go

Go does not provide a built-in linked list type, so you must define your own struct to represent nodes and manually manage the pointers connecting them.

Go does not provide a built-in linked list type, so you must define your own struct to represent nodes and manually manage the pointers connecting them. The standard approach involves creating a Node struct with a Value and a Next pointer, then implementing methods to append or insert elements while updating these pointers.

Here is a practical implementation of a singly linked list with an Append method to add elements to the end:

package main

import "fmt"

// Node represents a single element in the linked list.
type Node struct {
	Value int
	Next  *Node
}

// LinkedList represents the head of the list.
type LinkedList struct {
	Head *Node
}

// Append adds a new node with the given value to the end of the list.
func (l *LinkedList) Append(value int) {
	newNode := &Node{Value: value}
	if l.Head == nil {
		l.Head = newNode
		return
	}
	current := l.Head
	for current.Next != nil {
		current = current.Next
	}
	current.Next = newNode
}

// Print traverses the list and prints values.
func (l *LinkedList) Print() {
	for current := l.Head; current != nil; current = current.Next {
		fmt.Printf("%d -> ", current.Value)
	}
	fmt.Println("nil")
}

func main() {
	list := &LinkedList{}
	list.Append(10)
	list.Append(20)
	list.Append(30)
	list.Print()
}

If you need a doubly linked list or more complex operations (like deletion or insertion at specific indices), you can extend the Node struct to include a Prev pointer. However, for most standard use cases in Go, the container/list package is the preferred solution because it is optimized, handles memory management safely, and provides a doubly linked list out of the box.

Using container/list is generally safer and less error-prone than a manual implementation:

package main

import (
	"container/list"
	"fmt"
)

func main() {
	l := list.New()
	
	// Push back (append)
	l.PushBack(10)
	l.PushBack(20)
	l.PushBack(30)
	
	// Iterate
	for e := l.Front(); e != nil; e = e.Next() {
		fmt.Printf("%d -> ", e.Value)
	}
	fmt.Println("nil")
}

Choose the manual struct approach if you need to learn the underlying mechanics or require a custom node structure with specific metadata. Use container/list for production code where performance and reliability are priorities, as it avoids common pointer errors and handles edge cases like empty lists automatically.