Подтвердить что ты не робот

Почему Go запрещает брать адрес (&) члена карты, но разрешает (&) элемент фрагмента?

Go не позволяет брать адрес члена карты:

// if I do this:
p := &mm["abc"]
// Syntax Error - cannot take the address of mm["abc"]

Обоснование заключается в том, что если Go позволяет принимать этот адрес, когда база backstore растет или загорается, адрес может стать недействительным, запутать пользователя.

Но кусок Go перемещается, когда он перерастает его емкость, но Go позволяет нам взять адрес элемента среза:

 a := make([]Test, 5)
 a[0] = Test{1, "dsfds"}
 a[1] = Test{2, "sdfd"}
 a[2] = Test{3, "dsf"}

 addr1 := reflect.ValueOf(&a[2]).Pointer()
 fmt.Println("Address of a[2]: ", addr1)

 a = append(a, Test{4, "ssdf"})
 addrx := reflect.ValueOf(&a[2]).Pointer()
 fmt.Println("Address of a[2] After Append:", addrx)

 // Note after append, the first address is invalid
 Address of a[2]:  833358258224
 Address of a[2] After Append: 833358266416

Почему Go спроектирован так? Что особенного в адресе элемента среза?

4b9b3361

Ответ 1

Существует большая разница между срезами и картами: срезы поддерживаются массивом поддержки, а карт нет.

Если карта растет или сжимается, потенциальный указатель на элемент карты может стать свисающим указателем, указывающим в никуда (неинициализированная память). Проблема здесь заключается не в "путанице пользователя", а в том, что она сломает основной элемент дизайна Go: Нет оборванных указателей.

Если срез заканчивается, создается новый массив большего размера, и старый массив поддержки копируется в новый; и старый массив поддержки остается существующим. Таким образом, любые указатели, полученные из "незаросшего" фрагмента, указывающего на старый массив поддержки, по-прежнему являются действительными указателями на действительную память.

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

Обратите внимание, что при сокращении среза нет "уменьшения массива поддержки среза".

Ответ 2

Фундаментальное различие между map и slice заключается в том, что карта представляет собой динамическую структуру данных, которая перемещает значения, которые она содержит по мере ее роста. Конкретная реализация карты Go может даже возрастать постепенно, немного во время операций вставки и удаления, пока все значения не будут перемещены в большую структуру памяти. Таким образом, вы можете удалить значение, и вдруг может измениться другое значение. С другой стороны, срез - это просто интерфейс/указатель на подмассиву. Кусочек никогда не растет. Функция append может скопировать фрагмент в другой фрагмент с большей емкостью, но он оставил старый фрагмент неповрежденным и также является функцией вместо оператора индексирования.

В словах самого редактора карт:

https://www.youtube.com/watch?v=Tl7mi9QmLns&feature=youtu.be&t=21m45s "Это мешает этой растущей процедуре, поэтому, если я возьму адрес какой-то записи в ведре, а затем я оставляю эту запись для в то время как карта растет, то вдруг это указатель указывает на старое ведро, а не на новое ведро и этот указатель теперь недействителен, поэтому трудно обеспечить возможность адрес значения на карте, без ограничения того, как работает произведение... С++ растет по-другому, поэтому вы можете взять адрес ведра"

Итак, даже если & m [x] можно было бы разрешить и было бы полезно для короткоживущих операций (выполните модификацию значения, а затем не используйте этот указатель снова), и на самом деле карта внутренне делает это, Я думаю, что разработчики/разработчики языка решили быть в безопасности с картой, не позволяя & m [x], чтобы избежать тонких ошибок с программами, которые могут долго удерживать указатель, не понимая, тогда это указывает на разные данные, чем думал программист.

См. также Почему "Не разрешать" разрешает принимать адрес значения карты? для связанных комментариев.