Ошибки, которые стоит избегать на собеседовании Go
Сегодня мы разберём классическую задачу на собеседовании для GoLang-разработчика, связанную с указателями и передачей аргументов в функции по значению. В ходе разбора инспировщик наглядно объясняет, почему при передаче указателя в функцию изменяется лишь локальная копия указателя, а не оригинальный объект, и демонстрирует, как использование указателя на указатель решает эту проблему. Это простое, но крайне важное упражнение помогает глубже понять работу памяти, стековых фреймов и механизмов передачи данных в Go.
Вопрос 1. Что выведет код с функцией changeName, принимающей указатель на структуру Person?
Таймкод: 00:00:00
Ответ собеседника: Правильный. Код выведет два раза "Иван", потому что в Go аргументы передаются по значению. Указатель копируется в функцию, и изменение локальной копии указателя не влияет на оригинальный указатель в main.
Правильный ответ:
В данном вопросе ключевой момент — понимание разницы между переприсваиванием указателя и изменением данных по указателю.
Передача указателя по значению в Go
В Go всё передаётся по значению, включая указатели. Когда функция принимает *Person, она получает копию указателя. Это означает:
- Копия указателя указывает на тот же самый объект в памяти
- Если функция изменяет поля объекта через указатель (
p.Name = "Владимир"), оригинал изменится - Если функция переприсваивает сам указатель (
p = &Person{...}), изменится только локальная копия
Пример кода, который НЕ изменит оригинал:
package main
import "fmt"
type Person struct {
Name string
Age int
}
// Переприсваивание указателя — НЕ изменит оригинал
func changeName(p *Person) {
p = &Person{Name: "Владимир", Age: 25} // меняем локальную копию указателя
}
func main() {
person := Person{Name: "Иван", Age: 30}
fmt.Println(person.Name) // Иван
changeName(&person)
fmt.Println(person.Name) // Иван — не изменилось!
}
Пример кода, который ИЗМЕНИТ оригинал:
// Изменение полей через указатель — ИЗМЕНЯЕТ оригинал
func changeName(p *Person) {
p.Name = "Владимир" // меняем поле объекта, на который указывает копия
p.Age = 25
}
func main() {
person := Person{Name: "Иван", Age: 30}
fmt.Println(person.Name) // Иван
changeName(&person)
fmt.Println(person.Name) // Владимир — изменилось!
}
Как всё-таки переприсвоить указатель из функции
Если нужно именно заменить весь объект, есть два подхода:
// Способ 1: Указатель на указатель
func changeName(pp **Person) {
*pp = &Person{Name: "Владимир", Age: 25}
}
func main() {
person := &Person{Name: "Иван", Age: 30}
changeName(&person)
fmt.Println(person.Name) // Владимир
}
// Способ 2: Возврат нового указателя (идиоматичнее в Go)
func changeName(p *Person) *Person {
return &Person{Name: "Владимир", Age: 25}
}
func main() {
person := &Person{Name: "Иван", Age: 30}
person = changeName(person)
fmt.Println(person.Name) // Владимир
}
Вывод: Ответ собеседника верен — при переприсваивании указателя внутри функции оригинал не изменится, выведется "Иван" дважды. Для изменения оригинала нужно либо модифицировать поля объекта через указатель, либо использовать указатель на указатель, либо возвращать новый указатель.
