Go: Встроить примитивный тип?

Предположим, у нас есть этот кусок кода:

type User struct {
    int32
    Name string
}

Может ли этот тип встраивания быть полезным?
Есть ли у int32 какие-либо методы, которые другие могут вызывать для экземпляров User?
Как я могу получить доступ к значению int32, которое встраивает User?


person Mahdi    schedule 07.08.2016    source источник
comment
Вы можете использовать User в качестве этого типа. if user > 0 ... например. Если бы у примитива были методы, они бы тоже продвигались.   -  person eduncan911    schedule 07.08.2016
comment
@eduncan911 eduncan911 можете вставить пример кода? Я не смог сделать это на Go Playground.   -  person Mahdi    schedule 07.08.2016
comment
@eduncan911 Нет, так нельзя: u > int32(0) приводит к invalid operation: u > int32(0) (mismatched types User and int32)   -  person icza    schedule 07.08.2016
comment
Ах, верно. Вам по-прежнему необходимо получить доступ к ресурсу по имени: play.golang.org/p/mIk_7ewRkN   -  person eduncan911    schedule 07.08.2016


Ответы (1)


Тип int32 является предварительно объявленным типом, у него нет методов. Проверять:

fmt.Println(reflect.TypeOf(int32(0)).NumMethod()) // Prints 0

Вы можете ссылаться на все встроенные поля, используя неполное имя типа в качестве имени поля (Спецификация: типы структур), предварительно объявленные типы не являются исключением. См. этот пример:

u := User{3, "Bob"}
fmt.Printf("%#v\n", u)
u.int32 = 4
fmt.Println(u.int32)

Вывод (попробуйте на Go Playground):

main.User{int32:3, Name:"Bob"}
4

Основным преимуществом использования встраивания является:

  • методы встроенного типа продвигаются, поэтому проще реализовать интерфейсы (вам не нужно предоставлять методы, которые продвигаются)

  • вы можете «переопределить» методы встроенного типа (по типу встраивания): предоставьте свою собственную реализацию, которая будет вызываться при использовании значения вашего типа встраивания.

  • и поля встроенного типа продвигаются вперед, поэтому код короче для ссылки на продвигаемые поля (имя поля опущено).

Встраивая предварительно объявленные типы, такие как int32, вы не получаете никаких преимуществ по сравнению с использованием обычного поля (именованного, а не встроенного поля), поскольку тип int32 не имеет методов или полей.

Забегая вперед, помимо того, что у вас нет никакого преимущества, у вас есть еще и недостаток. Поскольку имена предварительно объявленных типов начинаются с букв нижнего регистра, их встраивание неявно делает их неэкспортируемыми, поэтому вы можете ссылаться на них только в объявлении пакета типа встраивания. Если вы сделаете их обычными, именованными полями, вы можете использовать имя в верхнем регистре, чтобы оно экспортировалось, или имя в нижнем регистре, чтобы сделать его неэкспортируемым.

person icza    schedule 07.08.2016
comment
Интересное использование тегов '‹kbd›' - person Goodbye StackExchange; 07.08.2016
comment
Интересно, что примитивные типы, которые содержат пробелы или символы (массивы/срезы, карты, каналы) не могут быть встроены таким образом, нужен typedef (объявление типа), вроде type MapStringInt map[string]int или type RecvChanInt <-chan int. - person Siu Ching Pong -Asuka Kenji-; 08.08.2016
comment
@SiuChingPong-AsukaKenji- На самом деле это следствие того, что неполное имя типа становится именем поля, а такие символы, как [ ] < -, не допускаются в идентификаторы. - person icza; 08.08.2016
comment
Ага, я это заметил. Но, к сожалению, продвигаются только методы анонимных полей, а не операторы. Таким образом, даже если анонимный RecvChanInt (он же <-chan int) встроен в User выше, все равно нельзя сделать <- u, а только <- u.RecvChanInt. - person Siu Ching Pong -Asuka Kenji-; 08.08.2016
comment
@SiuChingPong-AsukaKenji- И это потому, что это было бы двусмысленно. Представьте, что вы встраиваете 2 канала (2 разных типа с каналами в качестве базовых типов) и пишете <- u. Какой канал это может означать? - person icza; 08.08.2016
comment
Та же проблема (неоднозначность) существует и в текущем встраивании, так что разрешение этого НЕ делает его хуже: play. golang.org/p/tBaNUbCXbF - person Siu Ching Pong -Asuka Kenji-; 08.08.2016
comment
@SiuChingPong-AsukaKenji- Верно. Исправляю свой комментарий: нельзя, потому что операнд оператора приема должен быть канального типа, а структура явно не из таких. - person icza; 08.08.2016
comment
Да, конечно, на текущем языке Go. Как я уже сказал, [[Но, к сожалению, продвигаются только методы анонимных полей, а не операторы.]]. Было бы неплохо, если бы это было разрешено, поскольку одна из наиболее востребованных функций — выполнение range для пользовательского типа. - person Siu Ching Pong -Asuka Kenji-; 08.08.2016
comment
@FrankerZ Это использование давно было объявлено очень неправильным: meta.stackexchange.com/questions/181774/ - person Denys Séguret; 19.08.2016
comment
@DenysSéguret Я не знал об этом. Спасибо, что дали мне знать. - person icza; 19.08.2016