Двойной указатель решает конкретную задачу: изменить указатель внутри функции так, чтобы изменение было видно снаружи.
Проблема без
**:func resetPointer(p *int) {
newVal := 0
p = &newVal // меняем локальную копию — снаружи ничего не изменится
}p — это копия адреса. Переназначение p внутри функции не затрагивает оригинал.Решение через
**int:func resetPointer(p **int) {
newVal := 0
*p = &newVal // меняем сам указатель — изменение видно снаружи
}
func main() {
a := 42
ptr := &a
resetPointer(&ptr)
fmt.Println(*ptr) // 0
}Теперь функция получает адрес самого указателя и может подменить его цель.
Другие кейсы
Динамические структуры данных — при построении связных списков или деревьев, когда нужно менять корневой указатель:
func insertHead(head **Node, val int) {
newNode := &Node{val: val, next: *head}
*head = newNode
}Инициализация через функцию — паттерн, когда объект создаётся внутри функции и возвращается через параметр, а не через return:
func initConfig(cfg **Config) {
*cfg = &Config{Timeout: 30}
}Когда не нужен
Если цель просто изменить значение, на которое уже указывает указатель, достаточно одного
*. Двойной указатель нужен только тогда, когда требуется изменить сам адрес, хранящийся в указателе.🐸 Библиотека Go для собеса