Какой смысл иметь указатели в Go?

Я знаю, что указатели в Go допускают мутацию аргументов функции, но не было бы проще, если бы они принимали только ссылки (с соответствующими константными или изменяемыми квалификаторами). Теперь у нас есть указатели, а для некоторых встроенных типов, таких как карты и каналы, неявный проход по ссылке.

Я пропустил что-то или указатели в Go просто ненужное осложнение?

Ответ 1

Ссылки не могут быть переназначены, в то время как указатели могут. Это само по себе делает ссылки полезными во многих ситуациях, когда ссылки не могут быть использованы.

Ответ 2

Мне действительно нравится пример из http://www.golang-book.com/8

func zero(x int) {
    x = 0
}
func main() {
    x := 5
    zero(x)
    fmt.Println(x) // x is still 5
}

в отличие от

func zero(xPtr *int) {
    *xPtr = 0
}
func main() {
    x := 5
    zero(&x)
    fmt.Println(x) // x is 0
}

Ответ 3

Go разработан, чтобы быть кратким, минималистичным языком. Поэтому он начинался с простых значений и указателей. Позже, по необходимости, были добавлены некоторые ссылочные типы (фрагменты, карты и каналы).


Язык программирования Go: Language Design FAQ: Почему ссылки на карты, фрагменты и каналы в то время как массивы являются значениями?

"В этой теме много истории. Раньше карты и каналы были синтаксически указателями, и невозможно было объявить или использовать экземпляр не указателя. Кроме того, мы боролись с тем, как массивы должны работать. В конце концов мы решили, что строгое разделение указателей и значений затрудняло использование языка. Представляя ссылочные типы, включая срезы для обработки ссылочной формы массивов, разрешали эти проблемы. Типы ссылок добавляют некоторую печальную сложность в язык, но они оказывают большое влияние на удобство использования: Когда они были введены, Go стал более продуктивным и удобным языком".


Быстрая компиляция - главная цель дизайна языка программирования Go; который имеет свои издержки. Одной из жертв является способность отмечать переменные (за исключением базовых констант времени компиляции) и параметры как неизменные. Он был запрошен, но отклонен.


golang-nuts: go language. Некоторые отзывы и сомнения.

"Добавление const к системе типов заставляет его появляться повсюду и заставляет его удалять его повсюду, если что-то меняется. Хотя там может быть некоторой выгодой для обозначения объектов, неизменяемых каким-то образом, мы не подумайте, что спецификатор типа const должен идти ".

Ответ 4

Указатели полезны по нескольким причинам. Указатели позволяют управлять макетом памяти (влияет на эффективность кэша процессора). В Go мы можем определить структуру, в которой все члены находятся в смежной памяти:

type Point struct {
  x, y int
}

type LineSegment struct {
  source, destination Point
}

В этом случае структуры Point встроены в структуру LineSegment. Но вы не всегда можете напрямую вставлять данные. Если вы хотите поддерживать такие структуры, как бинарные деревья или связанный список, то вам нужно поддерживать какой-то указатель.

type TreeNode {
  value int
  left  *TreeNode
  right *TreeNode
}

Java, Python и т.д. не имеют этой проблемы, потому что не позволяют вставлять составные типы, поэтому нет необходимости синтаксически различать вложение и наведение.

Проблемы с Swift/С# -структурами, решенными с помощью указателей Go

Возможной альтернативой выполнению этого является различие между struct и class как С# и Swift. Но у этого есть ограничения. Хотя обычно можно указать, что функция принимает структуру как параметр inout, чтобы избежать копирования структуры, она не позволяет хранить ссылки (указатели) на структуры. Это означает, что вы никогда не сможете рассматривать структуру как ссылочный тип, когда найдете это полезным, например, для создания распределителя пула (см. ниже).

Пользовательский блок памяти

С помощью указателей вы также можете создать свой собственный распределитель пула (это очень упрощено с большим количеством проверок, удаленных, чтобы просто показать принцип):

type TreeNode {
  value int
  left  *TreeNode
  right *TreeNode

  nextFreeNode *TreeNode; // For memory allocation
}

var pool [1024]TreeNode
var firstFreeNode *TreeNode = &pool[0] 

func poolAlloc() *TreeNode {
    node := firstFreeNode
    firstFreeNode  = firstFreeNode.nextFreeNode
    return node
}

func freeNode(node *TreeNode) {
    node.nextFreeNode = firstFreeNode
    firstFreeNode = node
}

Поменять два значения

Указатели также позволяют реализовать swap. Это заменяет значения двух переменных:

func swap(a *int, b *int) {
   temp := *a
   *a = *b
   *b = temp
}

Заключение

Java никогда не смогла полностью заменить С++ для системного программирования в таких местах, как Google, отчасти из-за того, что производительность не может быть настроена в той же степени из-за отсутствия возможности управления макетом и использованием памяти (промахи в кэше влияют на производительность значительно). Go нацелен на замену С++ во многих областях и, следовательно, должен поддерживать указатели.