Инициализация структуры Golang

Существует простая структура, подобная этой:

type Event struct {
    Id         int
    Name       string
}

В чем разница между этими двумя методами инициализации?

e1 := Event{Id: 1, Name: "event 1"}
e2 := &Event{Id: 2, Name: "event 2"}

Любой, почему я должен использовать любой из этих методов инициализации?

Ответ 1

Первый способ

e1 := Event{Id: 1, Name: "event 1"}

инициализирует переменную e1 как значение типа Event.

Второй

e2 := &Event{Id: 1, Name: "event1"}

инициализирует e2 как указатель на значение типа Event Как вы указали в комментариях, набор методов, определенных для значения данного типа, является подмножеством набора методов, определенных на указатель на значение этого типа. Это означает, что если у вас есть метод

func (e Event) GetName() string {
    return e.Name
}

тогда и e1, и e2 могут вызывать этот метод, но если у вас был другой метод, скажите:

func (e *Event) ChangeName(s string) {
    e.Name = s
}

Тогда e1 не сможет использовать метод ChangeName, а e2 -.

Это (e1 не может использовать метод ChangeName, в то время как e2) это не тот случай (хотя, возможно, это было на момент написания этой справки), благодаря @DannyChen за то, что он поднял этот вопрос. и @GilbertNwaiwu для тестирования и публикации в комментариях ниже.

(Чтобы обратиться к вычеркнутому разделу выше: набор методов, определенных для типа структуры, состоит из методов, определенных для типа, и указателей на тип.

Вместо этого Go теперь автоматически разыменовывает аргумент для метода, так что если метод получает указатель, Go вызывает метод для указателя на эту структуру, а если метод получает значение, Go вызывает метод для значения, на которое указывает эта структура. На данный момент моя попытка обновить этот ответ может упустить что-то важное в семантике, поэтому, если кто-то захочет исправить это или уточнить, не стесняйтесь добавлять комментарий, указывающий на более полный ответ. Вот небольшая игровая площадка, иллюстрирующая эту проблему: https://play.golang.org/p/JcD0izXZGz.

В некоторой степени это изменение в том, как указатели и значения работают в качестве аргументов для методов, определенных для функции, влияет на некоторые области дискурса ниже, но я оставлю остальное неотредактированным, если кто-то не предложит мне обновить его, так как оно кажется более или менее правильным в контекст общей семантики языков, которые передаются по значению против указателя.)

Что касается разницы между указателями и значениями, этот пример является иллюстративным, так как указатели обычно используются в Go, чтобы позволить вам изменять значения, на которые указывает переменная (но есть еще много причин, по которым можно использовать и указатели! Хотя для типичных использовать, это обычно твердое предположение). Таким образом, если вы определили ChangeName вместо этого как:

func (e Event) ChangeName(s string) {
    e.Name = s
}

Эта функция была бы не очень полезна, если ее вызывать для получателя значения, поскольку значения (не указатели) не будут сохранять изменения, внесенные в них, если они передаются в функцию. Это связано с областью языкового проектирования, касающейся назначения и передачи переменных: В чем разница между передачей по ссылке и передачей по значению?

Вы можете увидеть это на этом примере в Go Playground: https://play.golang.org/p/j7yxvu3Fe6

Ответ 2

Тип e1 - Event, тип e2 - *Event. Инициализация на самом деле одинакова (с использованием синтаксиса композитного литерала, также не уверен, что этот жаргон - Go или С# или оба?), Но с e2 вы используете "адрес оператора" &, чтобы он возвращал указатель на этот объект а не сам экземпляр.