Параметры — это копии
Чему вы научитесь:
- объяснять, почему функция меняет только копию своего параметра, и возвращать новое значение через
return; - возвращать из функции несколько результатов и отбрасывать лишние через
_; - писать рекурсивные функции с базовым случаем;
- понимать, где видна переменная и что такое затенение.

Функцию удобно представлять станком с копировальным аппаратом внутри. Вы кладёте на стекло лист с числом, станок что-то с этим числом делает и выдаёт готовый лист:
- то, что вы кладёте на стекло, — параметры функции;
- лист на выходе — результат, который она возвращает через
return.
У этого станка есть особенность: он никогда не трогает ваш лист. Перед работой он снимает с него копию и работает с копией, а оригинал так и лежит на стекле.
Посмотрим, как это выглядит в коде. Функция double удваивает n, а переменная x в main остаётся прежней:
package main
import "fmt"
func double(n int) {
n = n * 2
fmt.Println("inside:", n) // inside: 10
}
func main() {
x := 5
double(x)
fmt.Println("outside:", x) // outside: 5
}Передача по значению
Давайте разберём пример по строкам:
x := 5создаёт переменнуюxвmain.- При вызове
double(x)Go копирует значение аргументаxв параметрn. n = n * 2превращает копию в10.- Первый
Printlnпечатаетinside: 10. - Функция завершается, и второй
Printlnпечатаетoutside: 5.
В этом разборе встретились два слова, которые часто путают:
- параметр — имя в объявлении функции:
nвfunc double(n int); - аргумент — значение, которое передают при вызове:
xвdouble(x).
Такой способ передачи называют передачей по значению. Функция получает копию аргумента, а не саму переменную.
Станок снимает копию при каждом запуске, и Go тоже копирует аргумент при каждом вызове. Для числа это быстрая операция, и вы её даже не замечаете.
Этот вопрос часто задают на собеседовании: «Как Go передаёт аргументы — по значению или по ссылке?» Ответ собирается из двух частей:
- всегда по значению: Go копирует любой аргумент, а не только числа;
- присваивание параметру никогда не меняет переменную вызывающего кода.
Параметр — отдельная переменная
Параметр — обычная переменная функции:
- её можно читать, менять через
=и++, передавать в другие вызовы; - она создаётся при каждом вызове и исчезает, когда функция завершается;
- с переменной вызывающего кода она никак не связана.
Есть два случая, когда связь только кажется.
Выражение в аргументе
Аргументом может быть выражение, а не только переменная. При вызове double(x + 1) Go сначала вычисляет сумму 6 и копирует её значение в n. Функция видит только число и не знает, из чего его получили.
Одинаковые имена
Одинаковые имена тоже ничего не связывают. Если назвать параметр x, как переменную в main, переменных всё равно будет две. Каждая видна только в своей функции, и изменение одной не трогает другую.
Вернуть новое значение
Функция связана с вызывающим кодом только двумя путями:
- аргументы на входе;
- результат на выходе.
Чтобы вызывающий код получил изменённое значение, функция возвращает его через return. Вызывающий сам решает, что сделать с результатом: напечатать, записать в новую переменную или обратно в x.
package main
import "fmt"
func doubled(x int) int {
x = x * 2
return x
}
func main() {
x := 5
fmt.Println(x, doubled(x)) // 5 10
x = doubled(x)
fmt.Println(x) // 10
}Разберём main по строкам:
x := 5создаёт переменнуюxвmain.fmt.Println(x, doubled(x))вызываетdoubled: функция меняет свой параметрxи возвращает10, аxвmainостаётся5. Строка печатает5 10.x = doubled(x)записывает результат второго вызова обратно вx.fmt.Println(x)печатает10: переменная изменилась только после присваивания.
Станок выдал новый лист, а положили его на место старого вы сами.
Частая ошибка
Функция возвращает новое значение, но результат вызова никуда не записали:
x := 5
doubled(x) // ✗
fmt.Println(x)Программа компилируется и печатает 5. Функцию можно вызвать отдельной инструкцией, даже если она возвращает значение, и тогда результат пропадает. Блюдо приготовлено, но его никто не взял. Компилятор об этом не предупреждает, поэтому результат присваивайте явно: x = doubled(x).
Главное
- Параметр — копия аргумента: изменения внутри функции не видны снаружи.
- Go всегда передаёт аргументы по значению.
- Чтобы вызывающий код получил новое значение, функция возвращает его, а вызывающий присваивает:
x = doubled(x).
Подпишись, чтобы пройти курс
Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.