Теория

Указательместоположение в памяти, где хранится значение.
Значение указателя — адрес переменной.

Таким образом, с помощью указателя можно считывать (разыменовывать указатель) и изменять значение переменной косвенно, не юзая имя переменной.

Главное — понять, зачем поинтеры нужны. Use case-ы помогут в этом.

Use cases

Side effect

func main() {
	x := 4
 
	squareVar(x)
	fmt.Printf("[squareVar]\t%d\n", x)
 
	squarePointer(&x)
	fmt.Printf("[squarePointer]\t%d\n", x)
}
 
func squareVar(v int) {
	v *= v
}
 
func squarePointer(p *int) {
	*p *= *p
}
 
/* stdout:
[squareVar]     4
[squarePointer] 16
*/

Так происходит потому, что в функцию в виде аргумента передается копия переменной, а не сама переменная, ибо x находится в пределах main().
А в случае с указателями, в виде аргумента функции передается адрес значения в памяти, что дает возможность функции “грубо” изменить значение по адресу в памяти.

Экономия памяти

Будь x примере выше типом данных посложнее, типа, структурой, массивом и т.д. — передача параметров по значению (с помощью переменной) было бы неэффективно из-за копирования значения и выделения под него памяти.

Проверка на пустое значение

var wallet *int  // nil
 
func hasWallet(money *int) bool {
	return money != nil	 // false
}

wallet := 0 вернул бы true, поэтому именно указатели применими для таких случаев.
Пока указатель не указывает на перменную, он равен nil.

Package flag

Пакет flag использует указатели в качестве ключей.

import "flag"
 
var n = flag.Bool("n", false, "don't \n")
var sep = flag.String("s", "", "separator")
 
func main() {
	flag.Parse()
	fmt.Print(strings.Join(flag.Args(), *sep))
	if !*n {
		fmt.Println()
	}
}
  1. Переменные n и sep — это указатели на переменные-флаги, доступ к которым осуществляется косвенно с помощью *n и *sep.
  2. flag.Parse() присваивает значение созданным переменным n и sepтолько после этого можно использовать флаги в программе. flag.Parse() при ошибке выкидывает os.Exit(2).
  3. Аргументы, не являющиеся флагами, доступны через flag.Args() как срез строк []string.

new() function

Выражение new(T):

  1. Создает неименованную переменную типа T
  2. Инициализирует ее нулевым значением
  3. Возвращает ее адрес

Проще говоря, new(T) создает указатель на нулевое значение указанного типа.

new() является просто синтаксическим удобством. Наглядно — две идентичные по поведению функции:

func newInt() *int {
	return new(int)
}
 
func newInt() *int {
	// Здесь уже приходится давать имя переменной
	var dummy int
	return &dummy
}

new() для переменных структурных типов

В общем, new() используется относительно редко, ибо наиболее распространены неименованные переменные структурных типов, для которых есть более гибкий литеральный синтаксис.
Пример с указателями (ибо работа со структурами часто именно через них и выполняется):

pp := &Point{1, 2}

что эквивалентно:

pp := new(Point)
*pp = Point{1, 2}

Соус: Книга Язык программирования Go Глава 4. Составные типы 4.4 Структуры 4.4.1 Структурные литералы

Соус: Книга Язык программирования Go Глава 2. Структура программы 2.3 Переменные 2.3.3 Функция new


Соусы: