Ответ
В Go все переменные передаются в функции по значению (pass-by-value). Однако некоторые типы данных ведут себя как ссылочные, потому что значением, которое копируется, является не сам набор данных, а небольшой заголовок (дескриптор), содержащий указатель на базовую структуру данных. Изменение этой базовой структуры через копию дескриптора будет видно и в оригинале.
К таким типам относятся:
-
Срезы (slices)
- Срез — это структура из трех полей: указатель на нижележащий массив, длина (len) и ёмкость (cap). При передаче среза копируется эта структура, но указатель продолжает указывать на тот же массив.
a := []int{1, 2, 3} b := a // Копируется дескриптор, но не массив b[0] = 42 // Изменяется элемент в общем массиве // fmt.Println(a[0]) // Выведет 42
- Срез — это структура из трех полей: указатель на нижележащий массив, длина (len) и ёмкость (cap). При передаче среза копируется эта структура, но указатель продолжает указывать на тот же массив.
-
Мапы (maps)
- Мапа — это указатель на внутреннюю хеш-таблицу. При присваивании или передаче в функцию копируется только этот указатель.
m1 := map[string]int{"a": 1} m2 := m1 // m2 и m1 указывают на одну и ту же хеш-таблицу m2["a"] = 100 // Изменение видно через m1 // fmt.Println(m1["a"]) // Выведет 100
- Мапа — это указатель на внутреннюю хеш-таблицу. При присваивании или передаче в функцию копируется только этот указатель.
-
Каналы (channels)
- Канал, как и мапа, является указателем на внутреннюю структуру, управляющую буфером и горутинами.
ch1 := make(chan int) ch2 := ch1 // ch2 и ch1 ссылаются на один и тот же канал
- Канал, как и мапа, является указателем на внутреннюю структуру, управляющую буфером и горутинами.
-
Указатели (pointers)
- Это явный тип для хранения адреса памяти другой переменной. Они являются основой ссылочного поведения.
x := 10 p := &x // p хранит адрес переменной x *p = 20 // Разыменование указателя меняет значение x // fmt.Println(x) // Выведет 20
- Это явный тип для хранения адреса памяти другой переменной. Они являются основой ссылочного поведения.
-
Функции (functions)
- Функциональные значения (замыкания) также являются ссылочным типом. Они представляют собой указатель на код функции и, возможно, на окружение (захваченные переменные).
f1 := func() { fmt.Println("test") } f2 := f1 // f2 ссылается на ту же функцию f2()
- Функциональные значения (замыкания) также являются ссылочным типом. Они представляют собой указатель на код функции и, возможно, на окружение (захваченные переменные).
Особый случай: интерфейсы (interfaces) Интерфейсное значение — это структура из двух указателей: один на информацию о типе, другой — на сами данные. Поэтому они также проявляют ссылочное поведение.