Прежде всего, ваша функция copyPerson()
не соответствует своему названию.Он копирует некоторые поля Person
, но не (обязательно) все.Он должен был иметь имя copySomeFieldsOfPerson()
.
Чтобы скопировать полное значение структуры, просто присвойте значение структуры.Если у вас есть функция, получающая не указатель Person
, который уже является копией, просто верните ее адрес:
func copyPerson(p Person) *Person {
return &p
}
Вот и все, это скопирует все существующие и будущие поля Person
.
Теперь могут быть случаи, когда поля являются указателями или значениями, подобными заголовку (например, срезом), которые должны быть «отделены» от исходного поля (точнее, от указанного объекта), и в этом случае вы делаетенеобходимо выполнить ручную настройку, например
type Person struct {
Name string
Age int
Data []byte
}
func copyPerson(p Person) *Person {
p2 := p
p2.Data = append(p2.Data, p.Data...)
return &p2
}
или альтернативное решение, которое не делает еще одну копию p
, но все еще отсоединяет Person.Data
:
func copyPerson(p Person) *Person {
var data []byte
p.Data = append(data, p.Data...)
return &p
}
Конечно, есликто-то добавляет поле, которое также требует ручной обработки, это вам не поможет.
Вы также можете использовать неопубликованный литерал, например:
func copyPerson(p Person) *Person {
return &Person{
p.Name,
p.Age,
}
}
Это приведет к компиляцииошибка времени, если кто-то добавит новое поле к Person
, потому что неопубликованный составной структурный литерал должен перечислить все поля.Опять же, это не поможет вам, если кто-то поменяет поля, в которых новые поля могут быть назначены старым (например, кто-то поменяет 2 поля рядом друг с другом, имеющих одинаковый тип), также не одобренные литералы.
Лучше всего, если владелец пакета предоставит конструктор копирования рядом с определением типа Person
.Поэтому, если кто-то изменит Person
, он / она должен нести ответственность за поддержание CopyPerson()
в рабочем состоянии.И, как уже упоминалось, у вас уже должны быть модульные тесты, которые не пройдут, если CopyPerson()
не соответствует его названию.
Лучший жизнеспособный вариант?
Если вы не можете разместитьCopyPerson()
рядом с типом Person
и попросите его автора сохранить его, продолжить копирование значений структуры и ручную обработку полей указателей и заголовков.
И вы можете создать тип person2
который является «снимком» типа Person
.Используйте пустую глобальную переменную для получения оповещения во время компиляции, если исходный тип Person
изменится, и в этом случае исходный файл, содержащий copyPerson()
, откажется компилировать, так что вы будете знать, что он нуждается в настройке.
Вот как это можно сделать:
type person2 struct {
Name string
Age int
}
var _ = Person(person2{})
Пустое объявление переменной не будет компилироваться, если поля Person
и person2
не совпадают.
Вариант вышеупомянутогопроверка во время компиляции может заключаться в использовании типизированных nil
указателей:
var _ = (*Person)((*person2)(nil))