2015-04-30 6 views
-1

Я работал над образцом программы, чтобы ответить на другой вопрос здесь, на 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 

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

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

+0

Возможный дубликат [Получение ошибки: неизвестное поле в строковом литерале] (https://stackoverflow.com/questions/33098460/getting-error-unknown-field-in-struct-literal) –

ответ

4

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

Promoted fields act like ordinary fields of a struct except that they cannot be used as field names in composite literals of the struct.

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

EDIT: Вот пример, где такой подход может иметь неприятные последствия:

Подумайте о случае, когда у вас есть вложение B, и экземпляр А вы хотите внедрить:

type A { 
    X int 
} 

type B { 
    A 
} 

Это достаточно просто

b := B{ X: 1 } 

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

a := A { X: 1 } 

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

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

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

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

+1

Apt quote of spec:). Но я не следую твоим предположениям по этой причине. Я не вижу большой разницы в поддержке 'b: = B {FName:" x "," LName: "y"} ', чем он поддерживает' b: = B {}; b.FName = "x"; b.LName = "y" '. –

+0

Да, эта цитата ясно указывает, что мой код не будет компилироваться. Я согласен с @DaveC, хотя ... Если это было сознательное дизайнерское решение, это было неправильно. Комментарий Дэйва указывает на мой ОСНОВНАЯ проблема с дизайном.В одной строке я обращаюсь к полю без жалобы, а другой компилятор утверждает, что он не существует в этой структуре. Это принципиально противоречие. – evanmcdonnal

+0

@DaveC см. Мое редактирование для примера, где это может быть плохо –