Ответ
В Go все аргументы в функции передаются исключительно по значению (pass by value). Это означает, что функция всегда получает копию передаваемой переменной, а не саму переменную.
Однако поведение зависит от типа данных:
-
Простые типы (int, string, bool, struct): Создается полная копия значения. Изменения этой копии внутри функции не влияют на исходную переменную.
-
Ссылочные типы (слайсы, мапы, каналы, указатели): Копируется не сам базовый массив данных или структура, а значение-дескриптор (для слайсов, мап) или адрес в памяти (для указателей). Этот дескриптор или указатель по-прежнему ссылается на ту же самую область памяти, что и оригинал.
- Слайсы и мапы: Изменение элементов слайса или мапы внутри функции будет видно снаружи, так как копия дескриптора указывает на тот же базовый массив данных.
- Указатели: Изменение значения, на которое указывает указатель, также будет видно снаружи.
Пример для наглядности:
package main
import "fmt"
// Принимает копию int. Изменения не видны снаружи.
func modifyValue(val int) {
val = 100
}
// Принимает копию указателя. Изменения по указателю видны снаружи.
func modifyPointer(ptr *int) {
*ptr = 100
}
// Принимает копию дескриптора слайса. Изменения элементов видны снаружи.
func modifySlice(slice []int) {
slice[0] = 100
}
func main() {
// Простой тип
num := 10
modifyValue(num)
fmt.Println("num после modifyValue:", num) // Вывод: 10
// Указатель
modifyPointer(&num)
fmt.Println("num после modifyPointer:", num) // Вывод: 100
// Слайс
s := []int{1, 2, 3}
modifySlice(s)
fmt.Println("slice после modifySlice:", s) // Вывод: [100 2 3]
}
Вывод: Хотя технически передача всегда идет по значению, для ссылочных типов эффект похож на передачу по ссылке, так как копируется не сама структура данных, а указатель на нее.
Ответ 18+ 🔞
А, ну вот, опять про эту передачу по значению в Go, блядь! Сидят, сука, дядьки с бородами, в свитерах, и такие: "Всё по значению, всё честно!" А потом новичок приходит, меняет в функции слайс, а снаружи всё поменялось — и он нихуя не понимает, ёпта! Сейчас разжуем, как бабушка котлету.
Слушай сюда, главное правило, которое вбить надо, как гвоздь в жопу: в Go всё, абсолютно всё, передаётся в функцию по значению. То есть, всегда делается копия. ВСЕГДА. Но вот тут-то, сука, и начинается магия, а точнее — ёбаный подвох, потому что копируется не всегда то, что ты думаешь.
Разберём по полочкам, как в армии гимнастёрку:
-
Простые типы (int, string, bool, struct): Тут всё прозрачно, как слёзы ребёнка. Копируется само число, сама строка, сама структура. Поменял внутри функции — и похуй, снаружи всё как было. Оригинал в полной безопасности, как член в портмоне.
-
А вот ссылочные типы (слайсы, мапы, каналы, указатели): А вот тут, блядь, ёперный театр! Копируется не сам массив данных или эта здоровенная мапа, а дескриптор (для слайса/мапы) или адрес памяти (для указателя). Это как если бы ты дал другу не свою квартиру, а копию ключа от неё. Квартиру-то он не унесёт (значение дескриптора скопировалось), но зайти и нассáть в твой горшок с фикусом — запросто!
- Слайсы и мапы: Меняешь элементы внутри — меняются и снаружи, потому что копия дескриптора тычет в тот же самый базовый массив, в ту же самую мапу. Это и создаёт иллюзию "передачи по ссылке", хотя технически — нет, блядь, по значению!
- Указатели: Ну тут вообще просто, как три копейки. Копируется адрес. По этому адресу заходишь и меняешь что хочешь. Оригинал, естественно, меняется. Волшебства ноль, чистая механика.
Ладно, хватит трепаться, вот тебе живой пример, смотри и вникай:
package main
import "fmt"
// Принимает копию числа. Меняй — не хочу, снаружи хоть трава не расти.
func modifyValue(val int) {
val = 100 // Поменяли копию. И что? Ничего.
}
// Принимает копию АДРЕСА. По этому адресу лезем и крушим всё нахуй.
func modifyPointer(ptr *int) {
*ptr = 100 // А вот это уже больно. Меняем значение по оригинальному адресу.
}
// Принимает копию ДЕСКРИПТОРА слайса. Дескриптор тычет в старый массив. Меняем элементы в нём.
func modifySlice(slice []int) {
slice[0] = 100 // Бах! И оригинальный слайс снаружи уже с другим первым элементом.
}
func main() {
// Простой тип — копия значения, тихо и мирно.
num := 10
modifyValue(num)
fmt.Println("num после modifyValue:", num) // 10. Как был паинькой, так и остался.
// Указатель — копия адреса, но по нему бьём точно в цель.
modifyPointer(&num)
fmt.Println("num после modifyPointer:", num) // 100. Вот тебе и "по значению", ёпта!
// Слайс — копия дескриптора, но доступ к данным тот же.
s := []int{1, 2, 3}
modifySlice(s)
fmt.Println("slice после modifySlice:", s) // [100 2 3]. Сюрприз, блядь! Данные-то поменялись!
}
Итог, блядь, какой? Да, передача всегда по значению, это свято. Но для "хитрых" типов (слайсы, мапы, указатели) копируется не груда данных, а лишь доступ к ней. Поэтому внутри функции можешь навести шороху в оригинальных данных. Запомни это, а то будешь потом, как Герасим, мучаться: "Му-му, а почему у меня слайс изменился?!"