Почему мутация в этом методе не сохраняется, и как это исправить?
Предскажите, что напечатает программа, объясните почему и исправьте так, чтобы изменение сохранилось.
type BasicStruct struct{ Value string }
func (b BasicStruct) Set(s string) { b.Value = s }
func (b BasicStruct) Get() string { return b.Value }
type Doer interface {
Set(string)
Get() string
}
func doChangeAndPrint(d Doer) {
d.Set("bar")
fmt.Println(d.Get())
}
func main() {
bs := BasicStruct{Value: "foo"}
doChangeAndPrint(bs)
}
Объясните вывод и исправьте так, чтобы печаталось bar.
Печатает foo. Вызов метода — это сахар для передачи получателя аргументом, а у Set значимый получатель, поэтому b — копия: b.Value = s меняет копию, а оригинал не трогается. Даже передача &bs всё равно напечатает foo, ведь Set копирует значение в любом случае. Исправление: сделать Set с указательным получателем, func (b *BasicStruct) Set(s string), и передавать &bs.
- ✗Думать, что значимый получатель меняет структуру вызывающего, а не копию
- ✗Считать, что одна лишь передача
&bsчинит это без смены получателя - ✗Полагать, что код не компилируется, а не просто печатает неизменённое значение
- →Почему метод со значимым получателем входит в набор методов и
T, и*T? - →После смены
Setна указательный получатель, почему надо передавать&bs, а неbs?
Что выведет
Программа печатает foo.
Вызов метода — это синтаксический сахар: bs.Set("bar") эквивалентно Set(bs, "bar"). Получатель b BasicStruct — значимый, поэтому в метод попадает копия структуры. Присваивание b.Value = s меняет копию, а bs в main остаётся {Value: "foo"}.
Тонкость: даже если передать doChangeAndPrint(&bs), вывод всё равно foo. Метод Set объявлен со значимым получателем, поэтому при вызове через указатель Go всё равно копирует значение в b.
Исправление
Сделайте Set методом с указательным получателем и передавайте указатель:
func (b *BasicStruct) Set(s string) { b.Value = s }
func main() {
bs := &BasicStruct{Value: "foo"}
doChangeAndPrint(bs) // печатает bar
}
Теперь *BasicStruct удовлетворяет Doer, а Set меняет ту самую структуру, на которую указывает bs. Методы со значимым получателем входят в набор методов и T, и *T; методы с указательным — только *T.