← Назад к вопросам

Каким образом аргументы передаются в функцию по умолчанию в Go?

1.0 Junior🔥 73 комментариев
#Основы Go

Комментарии (3)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Механизм передачи аргументов в функциях Go

В языке Go аргументы всегда передаются по значению (pass by value). Это фундаментальное правило, которое распространяется на все типы данных — как на примитивные типы (int, float, bool, string), так и на составные типы (структуры, массивы, срезы, мапы, указатели). Однако поведение при передаче сложных типов имеет свои нюансы из-за их внутреннего устройства.

Передача простых типов и структур

При передаче простых типов и структур (struct) создается полная копия значения. Изменения внутри функции не влияют на оригинальные переменные:

package main

import "fmt"

func modifyValue(x int) {
    x = 42
}

func main() {
    num := 10
    modifyValue(num)
    fmt.Println(num) // Вывод: 10 (значение не изменилось)
}

То же самое происходит со структурами:

type Person struct {
    Name string
    Age  int
}

func modifyStruct(p Person) {
    p.Name = "Изменено"
}

func main() {
    person := Person{Name: "Иван", Age: 30}
    modifyStruct(person)
    fmt.Println(person.Name) // Вывод: "Иван"
}

Передача указателей

Чтобы изменять оригинальные данные, необходимо явно передавать указатель (pointer). В этом случае по значению передается сам указатель (адрес памяти), но разыменование позволяет модифицировать исходный объект:

func modifyViaPointer(p *Person) {
    p.Name = "Изменено"
}

func main() {
    person := &Person{Name: "Иван", Age: 30}
    modifyViaPointer(person)
    fmt.Println(person.Name) // Вывод: "Изменено"
}

Важно понимать, что даже указатель передается по значению — копируется адрес, а не данные. Но так как обе копии (оригинальный и переданный указатели) ссылаются на одну область памяти, изменения через разыменование влияют на оригинал.

Особенности передачи ссылочных типов

Со срезами (slices), мапами (maps) и каналами (channels) ситуация интереснее. Эти типы internally содержат указатели на underlying data structures. При передаче по значению копируется дескриптор (заголовок), содержащий этот указатель, но не сами данные:

func modifySlice(s []int) {
    s[0] = 100 // Изменяет оригинальный массив
    s = append(s, 999) // Не влияет на оригинальный срез (может изменить длину/емкость локально)
}

func main() {
    slice := []int{1, 2, 3}
    modifySlice(slice)
    fmt.Println(slice) // Вывод: [100 2 3]
}

Ключевые моменты:

  • Изменение элементов среза/мапы внутри функции меняет оригинальные данные
  • Операции, изменяющие размер/емкость (append для срезов), могут создать новое underlying array и перестать влиять на оригинал
  • Мапы всегда передаются как указатели на runtime-структуру

Массивы — особый случай

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

func modifyArray(arr [3]int) {
    arr[0] = 100
}

func main() {
    array := [3]int{1, 2, 3}
    modifyArray(array)
    fmt.Println(array) // Вывод: [1 2 3] (не изменился)
}

Практические рекомендации

  1. Для структур большого размера предпочтительнее передавать указатель во избежание накладных расходов на копирование
  2. Срезы и мапы можно передавать как значения для большинства случаев — этого достаточно для модификации их содержимого
  3. Если нужно изменить сам срез (длину, емкость), передавайте указатель на срез (*[]int)
  4. Для неизменяемых данных передача по значению обеспечивает безопасность и предсказуемость
  5. Интерфейсы передаются как структура из двух указателей (на тип и на значение), что также является передачей по значению этих указателей

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

Каким образом аргументы передаются в функцию по умолчанию в Go? | PrepBro