Почему переприсваивание параметра-указателя не меняет указатель вызывающего?
Предскажите, что напечатает программа, и объясните почему. Затем измените changeName так, чтобы вторая строка печатала Alice.
type Person struct{ Name string }
func changeName(person *Person) {
person = &Person{Name: "Alice"}
}
func main() {
person := &Person{Name: "Bob"}
fmt.Println(person.Name)
changeName(person)
fmt.Println(person.Name)
}
Определите причину, затем исправьте.
Печатает Bob дважды. changeName получает копию указателя; присваивание person = &Person{...} лишь перенаправляет эту локальную копию, не трогая вызывающего. В Go нет передачи по ссылке. Исправление — менять через него: person.Name = "Alice".
- ✗Считать, что параметр-указатель позволяет переприсвоить сам указатель вызывающего внутри функции
- ✗Думать, что Go передаёт аргументы по ссылке, а не всегда по значению (включая значение указателя)
- ✗Полагать, что строка с переприсваиванием не компилируется, а не просто бесполезна для вызывающего
- →Как дать функции переуказать переменную вызывающего на совершенно новый
Person? - →Чем
*person = Person{Name: "Alice"}отличается отperson = &Person{...}?
Что выведет
Программа печатает Bob дважды.
В Go всё передаётся по значению, включая указатели. Параметр person *Person — это копия указателя вызывающего: обе копии поначалу указывают на одну и ту же структуру Person{Name: "Bob"}. Строка person = &Person{Name: "Alice"} присваивает локальной копии новый адрес — теперь она указывает на свежую структуру, но указатель в main по-прежнему держит старый адрес. Поэтому после возврата person.Name в main всё ещё Bob.
Это распространённая ловушка: "раз я передал указатель, значит работаю с оригиналом". Передача указателя даёт доступ к той структуре, на которую он указывает, а не к самой переменной-указателю вызывающего.
Исправление
Меняйте поле через указатель, не переприсваивая сам указатель:
func changeName(person *Person) {
person.Name = "Alice" // то же, что (*person).Name = "Alice"
}
Теперь функция пишет в структуру по общему адресу, и main печатает Bob, затем Alice. Если же нужно переуказать саму переменную вызывающего на новую структуру, передавайте указатель на указатель (**Person) или возвращайте новое значение и присваивайте его в main.