В Go нет ссылочных типов, как у вас в C ++.В Go все передается по значению.Когда термин «ссылочный тип» используется в Go, это означает тип, который ссылается на данные, которые они должны представлять (через указатели).
Срезы - это небольшие структурные структуры данных, представленные типом reflect.SliceHeader
:
type SliceHeader struct {
Data uintptr
Len int
Cap int
}
Содержит указатель на первый элемент среза в базовом массиве (поле SliceHeader.Data
).Эта структура небольшая и эффективна для передачи в качестве значения, нет необходимости передавать ее адрес (и разыменовывать ее для косвенного доступа к любому из ее полей).Элементы слайса хранятся не в заголовке слайса, а в массиве вне области памяти заголовка.Это означает, что изменение «остроконечного» элемента приведет к изменению элемента исходного среза.
Когда вы добавляете (более 0) элементов к срезу, поле Len
в заголовке должно измениться, поэтому новыйСрез, который описывает срез с дополнительными элементами, должен отличаться от того, который был до добавления, поэтому вам необходимо присвоить возвращаемое значение встроенной функции append()
.(Другие значения также могут измениться, но Len
обязательно должен измениться.)
Карты реализованы в виде указателей на структуру runtime.hmap
:
type hmap struct {
// Note: the format of the hmap is also encoded in cmd/compile/internal/gc/reflect.go.
// Make sure this stays in sync with the compiler's definition.
count int // # live cells == size of map. Must be first (used by len() builtin)
flags uint8
B uint8 // log_2 of # of buckets (can hold up to loadFactor * 2^B items)
noverflow uint16 // approximate number of overflow buckets; see incrnoverflow for details
hash0 uint32 // hash seed
buckets unsafe.Pointer // array of 2^B Buckets. may be nil if count==0.
oldbuckets unsafe.Pointer // previous bucket array of half the size, non-nil only when growing
nevacuate uintptr // progress counter for evacuation (buckets less than this have been evacuated)
extra *mapextra // optional fields
}
Как видите, этонамного более сложная структура данных, чем заголовок среза, и намного больше, передача этого значения не будет эффективной.
Добавление / удаление элементов (пар ключ-значение) из карты хранится в контейнерах, которыессылки на поля этой структуры, но поскольку карты обрабатываются как указатели под капотом, вам не нужно присваивать результат таких операций.
Для завершения каналы также реализованы как указатели, указывающиек типу runtime
пакета hchan
:
type hchan struct {
qcount uint // total data in the queue
dataqsiz uint // size of the circular queue
buf unsafe.Pointer // points to an array of dataqsiz elements
elemsize uint16
closed uint32
elemtype *_type // element type
sendx uint // send index
recvx uint // receive index
recvq waitq // list of recv waiters
sendq waitq // list of send waiters
// lock protects all fields in hchan, as well as several
// fields in sudogs blocked on this channel.
//
// Do not change another G's status while holding this lock
// (in particular, do not ready a G), as this can deadlock
// with stack shrinking.
lock mutex
}
Это опять-таки "толстая" структура, которая обрабатывается как значения карты.
См. связанные вопросы:
срез по сравнению с картой, используемой в параметре
Добавление к срезу с достаточной емкостью с использованием приемника значения
Передаются ли кусочки Голанга по значению?
Что означают "семантика значений" и "семантика указателей" в Go?