Составной литерал и поля встроенного типа

Я работал над примером программы, чтобы ответить на другой вопрос здесь, на SO, и был несколько озадачен тем фактом, что следующий код не будет компилироваться;

https://play.golang.org/p/wxBGcgfs1o

package main

import "fmt"

type A struct {
    FName string
    LName string
}

type B struct {
    A
}

func (a *A) Print() {
     fmt.Println(a.GetName())
}

func (a *A) GetName() string {
     return a.FName
}

func (b *B) GetName() string {
     return b.LName
}

func main() {
    a := &A{FName:"evan", LName:"mcdonnal"}
    b := &B{FName:"evan", LName:"mcdonnal"}

    a.Print()
    b.Print()
}

Ошибка есть;

/tmp/sandbox596198095/main.go:28: unknown B field 'FName' in struct literal
/tmp/sandbox596198095/main.go:28: unknown B field 'LName' in struct literal

Можно ли установить значение полей из встроенного типа в статическом инициализаторе? Как? Мне это кажется ошибкой компилятора; если бы у меня не было источников перед собой и я был знаком с типом, я бы бился головой об стену, говоря: "Очевидно, что на B существует FName, что говорит компилятор!?!?!".

Быстро, чтобы вытеснить типичные ответы, я знаю, что ближайший рабочий синтаксис это b := &B{A{FName:"evan", LName:"mcdonnal"}} но этот синтаксис, на мой взгляд, концептуально противоречит встраиванию, поэтому я был бы разочарован, если бы это был единственный вариант. Если это единственный способ, это недолгое появление компилятора Go или на самом деле существует теоретическое ограничение, которое не позволило бы компилятору интерпретировать синтаксис в моем нерабочем примере?

1 ответ

Решение

Это не ошибка компилятора, а дизайнерское решение. Спецификация языка просто гласит:

Продвигаемые поля действуют как обычные поля структуры, за исключением того, что они не могут использоваться в качестве имен полей в составных литералах структуры.

Я думаю, что причина этого заключается в том, чтобы избежать двусмысленности. Существует несколько правил для разрешения конфликтов имен при использовании селекторов, и они должны быть сложными, чтобы позволить то, что вы предлагаете. Кроме того, это может создать неоднозначность, если вы используете существующий экземпляр встроенной структуры внутри литерала структуры типа встраивания.

РЕДАКТИРОВАТЬ: Вот пример, где этот подход может иметь неприятные последствия:

Вспомните случай, когда у вас есть A, внедряющий B, и экземпляр A, который вы хотите встроить:

type A {
   X int
}

type B {
   A
}

Это достаточно просто сделать

b := B{ X: 1 } 

И сделайте вывод, что должно быть сделано. Но что, если у нас уже есть экземпляр A? Это не имеет смысла:

a := A { X: 1 }

b := B { X: 2, A: a, } 

Вы сначала присваиваете 2 нулевому экземпляру A, а затем назначаете инициализированный экземпляр A поверх него? И идентично ли это:

b := B { A: a, X: 2 }  ?

Это нарушает предположение, что порядок инициализации не имеет значения в составном литерале с именами полей.

Другие вопросы по тегам