четверг, 30 мая 2019 г.

Спецификация Go: блоки

Блок (block) - это, возможно пустая, последовательность объявлений (declarations) и операторов (statements) в соответствующих фигурных скобках.

Block = "{" StatementList "}" .
StatementList = { Statement ";" } .

Помимо явных блоков в исходном коде, существуют неявные блоки:

  • Всеобщий блок (universe block) охватывает весь исходный текст Go кода.
  • Каждый пакет имеет блок пакета, содержащий весь исходный текст Go кода для этого пакета.
  • Каждый файл имеет блок файлов, содержащий весь исходный текст Go кода в этом файле.
  • Каждый оператор if, for и switch считается находящимся в своем неявном блоке.
  • Каждое предложение в операторе "switch" или "select" действует как неявный блок.

Блоки являются гнездом (nest) для областей видимости и влияют на области видимости.


Читайте также:


Спецификация Go: свойства типов и значений - способность присвоения (assignability), представимость (representability)

Способность присвоения (Assignability)

Значение x способно к присваиванию переменной типа T ("x способно к присваиванию T", "x is assignable to T"), если применяется одно из следующих условий:

  • тип х идентичен T.
  • тип V значения x и тип T имеют идентичные базовые типы, и по крайней мере один из V или T не является определенным типом.
  • T является типом интерфейса, а x реализует T.
  • x - двунаправленное значение канала (bidirectional channel), T - тип канала, тип V значения x и T имеют идентичные типы элементов, и, по крайней мере, один из V или T не является определенным типом.
  • x - это предварительно объявленный идентификатор nil, а T - это указатель, функция, срез (slice), карта (map), канал или тип интерфейса.
  • x - нетипизированная константа, представимая значением типа T.

Представимость (Representability)

Константа x может быть представлена значением типа T, если выполняется одно из следующих условий:

  • х находится во множестве значений, определенных Т.
  • T является типом с плавающей точкой (floating-point type), и x может быть округлен до точности T без переполнения. При округлении используются правила округления до четности IEEE 754, но с отрицательным нулем IEEE, дополнительно упрощенным до нулевого знака. Обратите внимание, что постоянные значения никогда не приводят к отрицательному нулю IEEE, NaN или бесконечности.
  • T является сложным типом, а компоненты x: real(x) и imag(x) - представляются значениями типа компонента T (float32 или float64).

x                   T           x представима значением T потому что

'a'                 byte        97 это набор байтовых (byte) значений
97                  rune        rune это псевдоним int32, 
                                а 97 входит в набор 32-битных 
                                целых чисел (32-bit integers)
"foo"               string      "foo" входит в набор 
                                строовых (string) значений
1024                int16       1024 входит в набор 16-битных 
                                целых чисел (16-bit integers)
42.0                byte        42 входит в набор беззнаковых 
                                8-битных целых чисел 
                                (unsigned 8-bit integers)
1e10                uint64      10000000000 входит в набор 
                                беззнаковых 64-битных целых чисел 
                                (unsigned 64-bit integers)
2.718281828459045   float32     2.718281828459045 округляется 
                                до 2.7182817 которое входит 
                                в набор float32 значений
-1e-1000            float64     -1e-1000 округляется 
                                до IEEE -0.0 которое 
                                далее упрощается до 0.0
0i                  int         0 это целочисленное (integer) значение
(42 + 0i)           float32     42.0 (с нулевой мнимой (imaginary) частью) 
                                входит в набор float32 значений

x        T           x не представима значением T потому что

0        bool        0 не входит в набор boolean значений
'a'      string      'a' это rune, она не входит в набор 
                     строковых (string) значений
1024     byte        1024 не входит в набор беззнаковых 8-битных 
                     целых чисел (unsigned 8-bit integers)
-1       uint16      -1 не входит в набор беззнаковых 16-битных 
                     целых чисел (unsigned 16-bit integers)
1.1      int         1.1 это не цельночисловое (integer) значение
42i      float32     (0 + 42i) не входит в набор float32 значений
1e1000   float64     1e1000 переполняется до IEEE +Inf после округления


Читайте также:


Спецификация Go: свойства типов и значений - идентичность типа (Type identity)

Два типа могут быть либо идентичны, либо различны.

Определенный тип (defined type) всегда отличается от любого другого типа. В противном случае два типа идентичны, если литералы их базовых типов структурно эквивалентны; то есть они имеют одинаковую буквальную структуру, а соответствующие компоненты имеют идентичные типы. В деталях:

  • Два типа массива (array types) идентичны, если они имеют идентичные типы элементов и одинаковую длину массива.
  • Два типа срезов (slice types) идентичны, если они имеют идентичные типы элементов.
  • Два типа структуры (struct types) идентичны, если они имеют одинаковую последовательность полей и если соответствующие поля имеют одинаковые имена, идентичные типы и идентичные теги. Не экспортированные имена полей из разных пакетов всегда различны.
  • Два типа указателей идентичны, если они имеют идентичные базовые типы.
  • Два типа функций идентичны, если они имеют одинаковое количество параметров и значений результатов, соответствующие параметры и типы результатов идентичны, и либо обе функции являются переменными, либо ни одна из них не является. Имена параметров и результатов не обязательно должны совпадать.
  • Два типа интерфейса идентичны, если они имеют одинаковый набор методов с одинаковыми именами и одинаковыми типами функций. Не экспортированные имена методов из разных пакетов всегда различны. Порядок методов не имеет значения.
  • Два типа карт идентичны, если они имеют идентичные ключи и типы элементов.
  • Два типа каналов идентичны, если они имеют идентичные типы элементов и одинаковое направление.

Далее приведены декларации

type (
  A0 = []string
  A1 = A0
  A2 = struct{ a, b int }
  A3 = int
  A4 = func(A3, float64) *A0
  A5 = func(x int, _ float64) *[]string
)

type (
  B0 A0
  B1 []string
  B2 struct{ a, b int }
  B3 struct{ a, c int }
  B4 func(int, float64) *B0
  B5 func(x int, y float64) *A1
)

type  C0 = B0

Эти типы идентичны:

A0, A1, и []string
A2 и struct{ a, b int }
A3 и int
A4, func(int, float64) *[]string, и A5

B0 и C0
[]int и []int
struct{ a, b *T5 } и struct{ a, b *T5 }
func(x int, y float64) *[]string, func(int, float64) (result *[]string), и A5

B0 и B1 различны, потому что они являются новыми типами, созданными различными определениями типов; func(int, float64) *B0 и func(x int, y float64) *[]string отличаются тем, что B0 отличается от []string.


Читайте также:


среда, 29 мая 2019 г.

Спецификация Go: типы каналов (channel types)

Канал (channel) предоставляет одновременно выполняемым функциям механизм для обмена данными путем отправки и получения значений определенного типа элемента. Значение не инициализированного канала равно nil.

ChannelType = ( "chan" | "chan" "<-" | "<-" "chan" ) ElementType .

Необязательный оператор <- указывает направление канала, отправлять или получать. Если направление не указано, канал является двунаправленным. Канал может быть ограничен только для отправки или только для получения путем назначения или явного преобразования.

chan T          // может быть использован для отправки и получения значений типа T
chan<- float64  // может быть использован только для отправки float64s
<-chan int      // может быть использован только для получения ints

Оператор <- ассоциируется с самым левым возможным chan:

chan<- chan int    // то же самое что и chan<- (chan int)
chan<- <-chan int  // то же самое что и chan<- (<-chan int)
<-chan <-chan int  // то же самое что и <-chan (<-chan int)
chan (<-chan int)

Новое, инициализированное значение канала может быть создано с использованием встроенной функции make, которая принимает тип канала и необязательную емкость в качестве аргументов:

make(chan int, 100)

Емкость, в количестве элементов, устанавливает размер буфера в канале. Если пропускная способность равна нулю или отсутствует, канал не буферизуется, и связь устанавливается успешно только тогда, когда отправитель и получатель готовы. В противном случае канал буферизуется, и связь успешно завершается без блокировки, если буфер не заполнен (отправляет) или не пуст (получает). nil канал никогда не готов к общению.

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

Один канал может использоваться в операторах отправки, операциях приема и вызовах встроенных функций cap и len любым количеством процедур без дальнейшей синхронизации. Каналы действуют как очереди «первым пришел - первым обслужен» (first-in-first-out, FIFO). Например, если одна программа отправляет значения по каналу, а вторая программа получает их, значения принимаются в порядке отправки.


Читайте также:


Спецификация Go: типы карты (map types)

Карта (map) - это неупорядоченная группа элементов одного типа, называемая типом элемента (element type), индексируемая набором уникальных ключей другого типа, называемого типом ключа (key type). Значение не инициализированной карты равно nil.

MapType     = "map" "[" KeyType "]" ElementType .
KeyType     = Type .

Операторы сравнения == и != должны быть полностью определены для операндов типа ключа; таким образом, тип ключа не должен быть функцией, картой или срезом (slice). Если тип ключа является типом интерфейса, эти операторы сравнения должны быть определены для значений динамического ключа; сбой вызовет панику во время выполнения (run-time panic).

map[string]int
map[*T]struct{ x, y float64 }
map[string]interface{}

Количество элементов карты называется его длиной. Для карты m длину можно обнаружить с помощью встроенной функции len, и она может измениться во время выполнения (run-time). Элементы могут быть добавлены во время выполнения с использованием назначений и извлечены с помощью индексных выражений; они могут быть удалены с помощью встроенной функции delete.

Новое пустое значение карты создается с помощью встроенной функции make, которая принимает тип карты и необязательный указатель емкости в качестве аргументов:

make(map[string]int)
make(map[string]int, 100)

Начальная емкость не ограничивает ее размер: карты растут в соответствии с количеством элементов, хранящихся в них, за исключением нулевых карт (nil maps). Нулевая карта эквивалентна пустой карте, за исключением того, что никакие элементы не могут быть добавлены.


Читайте также:


вторник, 28 мая 2019 г.

Спецификация Go: тип интерфейса (interface type)

Тип интерфейса (interface type) определяет набор методов, называемый его интерфейсом. Переменная типа интерфейса может хранить значение любого типа с набором методов, который является любым надмножеством интерфейса. Говорят, что такой тип реализует интерфейс. Значение не инициализированной переменной типа интерфейса равно nil.

InterfaceType      = "interface" "{" { MethodSpec ";" } "}" .
MethodSpec         = MethodName Signature | InterfaceTypeName .
MethodName         = identifier .
InterfaceTypeName  = TypeName .

Как и во всех наборах методов, в типе интерфейса каждый метод должен иметь уникальное непустое имя.

// Простой File интерфейс
interface {
  Read(b Buffer) bool
  Write(b Buffer) bool
  Close()
}

Несколько типов могут реализовывать интерфейс. Например, если два типа S1 и S2 имеют набор методов

func (p T) Read(b Buffer) bool { return … }
func (p T) Write(b Buffer) bool { return … }
func (p T) Close() { … }

(где T обозначает либо S1, либо S2), тогда интерфейс File реализуется как S1, так и S2, независимо от того, какие другие методы S1 и S2 могут иметь или совместно использовать.

Тип реализует любой интерфейс, содержащий любое подмножество его методов, и поэтому может реализовывать несколько отдельных интерфейсов. Например, все типы реализуют пустой интерфейс:

interface{}

Точно так же рассмотрите следующую спецификацию интерфейса, которая появляется в объявлении типа, чтобы определить интерфейс с именем Locker:

type Locker interface {
  Lock()
  Unlock()
}

Если S1 и S2 также реализуют

func (p T) Lock() { … }
func (p T) Unlock() { … }

значит они реализуют интерфейс Locker, а также интерфейс File.

Интерфейс T может использовать (возможно, квалифицированное) имя типа интерфейса E вместо спецификации метода. Это называется встраиванием интерфейса E в T; он добавляет все (экспортируемые и не экспортируемые) методы E в интерфейс T.

type ReadWriter interface {
  Read(b Buffer) bool
  Write(b Buffer) bool
}

type File interface {
  ReadWriter  // то же самое что и добавление методов ReadWriter
  Locker      // то же самое что и добавление методов Locker
  Close()
}

type LockedFile interface {
  Locker
  File        // недопустимо: Lock, Unlock не уникальны
  Lock()      // недопустимо: Lock не уникален
}

Интерфейсный тип T не может быть встроенным сам по себе или любой интерфейсный тип, который встраивает T, рекурсивно.

// недопустимо: Bad не может встраивать сам себя
type Bad interface {
  Bad
}

// недопустимо: Bad1 не может встраивать сам себя используя Bad2
type Bad1 interface {
  Bad2
}
type Bad2 interface {
  Bad1
}


Читайте также:


Спецификация Go: типы указателя (pointer types), типы функций (function types)

Типы указателя (pointer types)

Тип указателя обозначает набор всех указателей на переменные данного типа, называемые базовым типом указателя. Значение неинициализированного указателя равно nil.

PointerType = "*" BaseType .
BaseType    = Type .

*Point
*[4]int

Типы функций (function types)

Тип функции (function type) обозначает набор всех функций с одинаковыми параметрами и типами результатов. Значение неинициализированной переменной типа функции равно nil.

FunctionType   = "func" Signature .
Signature      = Parameters [ Result ] .
Result         = Parameters | Type .
Parameters     = "(" [ ParameterList [ "," ] ] ")" .
ParameterList  = ParameterDecl { "," ParameterDecl } .
ParameterDecl  = [ IdentifierList ] [ "..." ] Type .

В списке параметров или результатов имена (IdentifierList) должны либо присутствовать, либо отсутствовать. Если присутствует, каждое имя обозначает один элемент (параметр или результат) указанного типа, и все непустые имена в подписи должны быть уникальными. Если отсутствует, каждый тип обозначает один элемент этого типа. Списки параметров и результатов всегда заключаются в круглые скобки, за исключением того, что если есть ровно один безымянный результат, он может быть записан как тип без скобок.

Последний входящий параметр в сигнатуре функции может иметь тип с ... префиксом. Функция с таким параметром называется variadic и может вызываться с нулевым или большим количеством аргументов для этого параметра.

func()
func(x int) int
func(a, _ int, z float32) bool
func(a, b int, z float32) (bool)
func(prefix string, values ...int)
func(a, b int, z float64, opt ...interface{}) (success bool)
func(int, int, float64) (float64, *[]int)
func(n int) func(p *T)


Читайте также:


понедельник, 27 мая 2019 г.

Спецификация Go: типы структур (struct types)

Структура (struct) - это последовательность именованных элементов, называемых полями, каждый из которых имеет имя и тип. Имена полей могут быть указаны явно (IdentifierList) или неявно (EmbeddedField). В структуре непустые имена полей должны быть уникальными.

StructType    = "struct" "{" { FieldDecl ";" } "}" .
FieldDecl     = (IdentifierList Type | EmbeddedField) [ Tag ] .
EmbeddedField = [ "*" ] TypeName .
Tag           = string_lit .

// Пустая структура.
struct {}

// Структура с 6 полями.
struct {
  x, y int
  u float32
  _ float32  // padding
  A *[]int
  F func()
}

Поле, объявленное с типом, но без явного имени поля, называется встроенным полем. Встроенное поле должно быть указано как имя типа T или как указатель на имя не интерфейсного типа *T, а само T может не быть указателем типа. Неквалифицированное (unqualified) имя типа действует как имя поля.

// Структура с 4 встроенными (embedded) полями типов T1, *T2, P.T3 и *P.T4
struct {
  T1        // имя поля T1
  *T2       // имя поля T2
  P.T3      // имя поля T3
  *P.T4     // имя поля T4
  x, y int  // имена полей x и y
}

Следующее объявление недопустимо, потому что имена полей должны быть уникальными в типе структуры:

struct {
  T     // конфликтует с встроенным (embedded) полем *T и *P.T
  *T    // конфликтует с встроенным (embedded) полем T и *P.T
  *P.T  // конфликтует с встроенным (embedded) полем T и *T
}

Поле или метод f встроенного поля в структуре x называется продвигаемым (promoted), если x.f является допустимым селектором, который обозначает это поле или метод f.

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

При заданном типе структуры S и определенном типе T продвигаемые методы включаются в набор методов структуры следующим образом:

  • Если S содержит встроенное поле T, наборы методов S и *S включают в себя продвигаемые методы с получателем T. Набор методов *S также включает продвигаемые методы с получателем *T.
  • Если S содержит встроенное поле *T, наборы методов S и *S оба включают продвигаемые методы с получателем T или *T.

За объявлением поля может следовать необязательный тег из строкового литерала, который становится атрибутом для всех полей в соответствующем объявлении поля. Пустая строка тега эквивалентна отсутствующему тегу. Теги становятся видимыми через интерфейс отражения (reflection interface) и принимают участие в идентификации типов для структур, но в остальном игнорируются.

struct {
  x, y float64 ""  // пустая строка в виде тега эквивалентна отсутствию тега
  name string  "люба строка допустима в виде тега"
  _    [4]byte "ceci n'est pas un champ de structure"
}

// Структура соответствующая буферу TimeStamp протокола.
// Теговые строки определяют номера поля буфера протокола;
// они следуют соглашению обозначенному в reflect пакете.
struct {
  microsec  uint64 `protobuf:"1"`
  serverIP6 uint64 `protobuf:"2"`
}


Читайте также:


пятница, 24 мая 2019 г.

Спецификация Go: типы массивов (array), типы срезов (slice)

Типы массивов (array types)

Массив (array) - это пронумерованная последовательность элементов одного типа, называемая типом элемента (element type). Количество элементов называется длиной массива и никогда не бывает отрицательным.

ArrayType   = "[" ArrayLength "]" ElementType .
ArrayLength = Expression .
ElementType = Type .

Длина является частью типа массива; она должна вычисляться как неотрицательная константа, представимая значением типа int. Длина массива a может быть определена с помощью встроенной функции len. Элементы могут быть адресованы целочисленными индексами от 0 до len(a)-1. Типы массивов всегда одномерны, но могут быть составлены для формирования многомерных типов.

Примеры:

[32]byte
[2*N] struct { x, y int32 }
[1000]*float64
[3][5]int
[2][2][2]float64  // то же самое что и [2]([2]([2]float64))

Типы срезов (slice types)

Срез (slice) является дескриптором непрерывного сегмента базового массива и обеспечивает доступ к пронумерованной последовательности элементов из этого массива. Тип среза обозначает набор всех срезов массивов его типа элемента. Количество элементов называется длиной среза и никогда не бывает отрицательным. Значение неинициализированного среза равно нулю.

SliceType = "[" "]" ElementType .

Длина среза s может быть обнаружена встроенной функцией len; в отличие от массивов, длина среза может измениться во время выполнения. Элементы могут быть адресованы целочисленными индексами от 0 до len(s)-1. Индекс среза данного элемента может быть меньше, чем индекс того же элемента в базовом массиве.

После инициализации срез всегда ассоциируется с базовым массивом, который содержит его элементы. Следовательно, срез совместно использует хранилище со своим массивом и с другими срезами того же массива; напротив, отдельные массивы всегда представляют собой разные хранилища.

Массив, лежащий в основе среза, может выходить за конец среза. Емкость (capacity) является мерой этой степени: это сумма длины среза и длины массива за пределами среза; срез длиной до этой емкости можно создать, нарезав новый из исходного среза. Емкость среза а можно определить с помощью встроенной функции cap(а).

Новое, инициализированное значение среза для данного типа элемента T создается с использованием встроенной функции make, которая принимает тип среза и параметры, указывающие длину и, необязательно, емкость. Срез, созданный с помощью make, всегда выделяет новый скрытый массив, на который ссылается возвращаемое значение среза. То есть выполнение

make([]T, length, capacity)

создает тот же срез, что и выделение массива и последующее создание срезов для него, поэтому эти два выражения эквивалентны:

make([]int, 50, 100)
new([100]int)[0:50]

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


Читайте также:


четверг, 23 мая 2019 г.

Спецификация Go: логические, числовые, строковые типы

Логический (boolean) тип

Логический (boolean) тип представляет собой набор логических (boolean) значений истинности, обозначаемых предварительно объявленными константами true и false. Предварительно объявленный логический тип - bool; это определенный тип.

Числовые (numeric) типы

Числовой тип представляет наборы целочисленных значений или значений с плавающей точкой (floating-point). Предварительно объявленные независимые от архитектуры числовые типы:

uint8       набор всех беззнаковых (unsigned)  8-битных целых чисел (integers) (от 0 до 255)
uint16      набор всех беззнаковых (unsigned) 16-битных целых чисел (integers) (от 0 до 65535)
uint32      набор всех беззнаковых (unsigned) 32-битных целых чисел (integers) (от 0 до 4294967295)
uint64      набор всех беззнаковых (unsigned) 64-битных целых чисел (integers) (от 0 до 18446744073709551615)

int8        набор всех 8-битных целых чисел (integers) со знаком (signed) (от -128 до 127)
int16       набор всех 16-битных целых чисел (integers) со знаком (signed) (от -32768 до 32767)
int32       набор всех 32-битных целых чисел (integers) со знаком (signed) (от -2147483648 до 2147483647)
int64       набор всех 64-битных целых чисел (integers) со знаком (signed) (от -9223372036854775808 до 9223372036854775807)

float32     набор всех IEEE-754 32-битных цисел с плавающей точкой (floating-point)
float64     набор всех IEEE-754 64-битных цисел с плавающей точкой (floating-point)

complex64   набор всех комплексных чисел с float32 реальной (real) и воображаемой (imaginary) частями
complex128  набор всех комплексных чисел с float64 реальной (real) и воображаемой (imaginary) частями

byte        псевдоним (alias) для uint8
rune        псевдоним (alias) для int32

Значение n-битного целого числа имеет ширину n бит и представлено с использованием арифметики с двумя дополнительными значениями.

Существует также набор предварительно объявленных числовых типов с размерами, зависящими от реализации:

uint     либо 32 либо 64 бита
int      тот же размер что и у uint
uintptr  беззнаковое целое число достаточно большое для хранения неинтерпретируемых битов значения указателя (pointer)

Во избежание проблем с переносимостью все числовые типы являются определенными типами и, следовательно, различаются, за исключением byte, который является псевдонимом для uint8, и rune, который является псевдонимом для int32. Явные преобразования необходимы, когда разные числовые типы смешаны в выражении или присваивании. Например, int32 и int не относятся к одному и тому же типу, даже если они имеют одинаковый размер в конкретной архитектуре.

Строковые типы (string types)

Строковый тип представляет собой набор строковых значений. Строковое значение - это (возможно, пустая) последовательность байтов. Количество байтов называется длиной строки и никогда не бывает отрицательным. Строки являются неизменяемыми: после создания невозможно изменить содержимое строки. Предварительно объявленный тип строки - строка (string); это определенный тип (defined type).

Длину строки s можно узнать с помощью встроенной функции len. Длина является константой времени компиляции (compile-time constant), если строка является константой. Байты строки могут быть доступны с помощью целочисленных индексов от 0 до len(s)-1. Недопустимо брать адрес такого элемента; если s[i] - это i-й байт строки, &s[i] недопустим.


Читайте также:


среда, 22 мая 2019 г.

Спецификация Go: типы (types)

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

Type      = TypeName | TypeLit | "(" Type ")" .
TypeName  = identifier | QualifiedIdent .
TypeLit   = ArrayType | StructType | PointerType | FunctionType | InterfaceType |
      SliceType | MapType | ChannelType .

Язык предварительно объявляет имена определенных типов. Другие представлены с объявлениями типа. Составные типы - массив (array), структура (struct), указатель (pointer), функция (function), интерфейс (interface), срез (slice), карта (map) и типы каналов (channel) - могут быть созданы с использованием литералов типа.

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

type (
  A1 = string
  A2 = A1
)

type (
  B1 string
  B2 B1
  B3 []B1
  B4 B3
)

Базовым типом строки (string), A1, A2, B1 и B2, является строка (string). Базовый тип []B1, B3 и B4 - это []B1.

Наборы методов (method sets)

Тип может иметь набор методов (method set), связанный с ним. Набор методов типа интерфейса является его интерфейсом. Набор методов любого другого типа T состоит из всех методов, объявленных с типом получателя T. Набор методов соответствующего типа указателя *T является набором всех методов, объявленных с получателем *T или T (то есть он также содержит набор методов T). Дополнительные правила применяются к структурам (struct), содержащим встроенные поля, будет описано в посте о типах структур. Любой другой тип имеет пустой набор методов. В наборе методов каждый метод должен иметь уникальное непустое имя метода.

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


Читайте также:


вторник, 21 мая 2019 г.

Спецификация Go: переменные (variables)

Переменная - это место хранения для значения. Набор допустимых значений определяется типом переменной.

Объявление переменной или, для параметров и результатов функции, подпись объявления функции или литерала функции резервирует хранилище для именованной переменной. Вызов встроенной функции new или получение адреса составного литерала выделяет память для переменной во время выполнения. На такую анонимную переменную ссылаются через (возможно, неявный) указатель косвенности (pointer indirection).

Структурированные переменные типа array, slice и struct имеют элементы и поля, которые могут быть адресованы индивидуально. Каждый такой элемент действует как переменная.

Статический тип (или просто тип) переменной - это тип, указанный в ее объявлении, тип, указанный в new вызове или составном литерале, или тип элемента структурированной переменной. Переменные типа интерфейса также имеют отдельный динамический тип, который является конкретным типом значения, назначенного переменной во время выполнения (если только это значение не является предварительно объявленным идентификатором nil, который не имеет типа). Динамический тип может изменяться во время выполнения, но значения, хранящиеся в интерфейсных переменных, всегда можно назначить статическому типу переменной.

var x interface{}  // x это nil и имеет статический тип interface{}
var v *T           // v имеет значение nil, статический тип *T
x = 42             // x имеет значение 42 и динамический тип int
x = v              // x имеет значение (*T)(nil) и динамический тип *T

Значение переменной извлекается путем обращения к переменной в выражении; это самое последнее значение, присвоенное переменной. Если переменной еще не присвоено значение, ее значением является нулевое значение для ее типа.


Читайте также:


Спецификация Go: константы

Существуют логические константы, рунные константы, целочисленные константы, константы с плавающей точкой, комплексные константы и строковые константы. Рунические, целочисленные, с плавающей точкой и комплексные константы вместе называются числовыми константами.

Значение константы представлено руническим, целым, плавающим, мнимым или строковым литералом, идентификатором, обозначающим константу, константным выражением, преобразованием с результатом, который является константой, или значением результата некоторых встроенных функций, таких как unsafe.Sizeof применяемой к любому значению, cap или len применяются к некоторым выражениям, real и imag применяются к комплексной константе, а complex применяется к числовым константам. Значения логической истинности представлены заранее объявленными константами true и false. Предварительно объявленный идентификатор йота (iota) обозначает целочисленную константу.

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

Числовые константы представляют собой точные значения произвольной точности и не переполняются. Следовательно, нет констант, обозначающих IEEE-754 отрицательный ноль, бесконечность и не числовые (not-a-number) значения.

Константы могут быть типизированными или нетипизированными. Литеральные константы, true, false, йота и некоторые константные выражения, содержащие только нетипизированные константные операнды, являются нетипизированными.

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

Нетипизированная константа имеет тип по умолчанию, который является типом, в который константа неявно преобразуется в контекстах, где требуется типизированное значение, например, в коротком объявлении переменной, таком как i := 0, где нет явного типа. Типом по умолчанию нетипизированной константы является bool, rune, int, float64, complex128 или string соответственно, в зависимости от того, является ли она логической, рунической, целой, с плавающей точкой, комплексной или строковой константой.

Ограничение реализации: хотя числовые константы имеют произвольную точность в языке, компилятор может реализовать их, используя внутреннее представление с ограниченной точностью. Тем не менее, каждая реализация должна:

  • Представлять целочисленные константы с как минимум 256 битами.
  • Представлять константы с плавающей точкой, включая части комплексной константы, с мантиссой не менее 256 бит и двоичным показателем со знаком не менее 16 бит.
  • Выдавать ошибку, если не может точно представить целочисленную константу.
  • Выдавать ошибку, если не может представить плавающую точку или комплексную константу из-за переполнения.
  • Округлять до ближайшей представимой константы, если не может представить с плавающей точкой или комплексную константу из-за ограничений по точности.

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


Читайте также:


понедельник, 20 мая 2019 г.

Спецификация Go: строковые литералы

Строковые литералы (String literals)

Строковый литерал представляет собой строковую константу, полученную в результате объединения последовательности символов. Существует две формы: необработанные строковые литералы (raw string literals) и интерпретируемые строковые литералы (interpreted string literals).

Необработанные строковые литералы - это символьные последовательности между обратными кавычками, как в `foo`. В кавычках может появляться любой символ, кроме обратной кавычки. Значение необработанного строкового литерала - это строка, состоящая из неинтерпретированных (неявно кодированных в UTF-8) символов между кавычками; в частности, обратная косая черта не имеет специального значения, и строка может содержать символы новой строки. Символы возврата каретки ('\r') внутри необработанных строковых литералов отбрасываются из необработанного строкового значения.

Интерпретируемые строковые литералы являются символьными последовательностями между двойными кавычками, как в "bar". В кавычках может появляться любой символ, кроме символа новой строки (newline) и неэкранированной двойной кавычки. Текст между кавычками формирует значение литерала, с экранированием обратной косой черты, интерпретируемым так же как в литералах рун (за исключением того, что \' недопустимо, а \" является законным), с теми же ограничениями. Трехзначный восьмеричный (\nnn) и двузначные шестнадцатеричные (\xnn) escape-символы представляют отдельные байты результирующей строки, все другие escape-последовательности представляют (возможно, многобайтовую) кодировку UTF-8 отдельных символов. Таким образом, внутри строкового литерала \377 и \xFF представляют один байт значения 0xFF = 255, тогда как ÿ, \u00FF, \U000000FF и \xc3\xbf представляют два байта 0xc3 0xbf кодировки UTF-8 символа U+00FF (символ ÿ).

string_lit             = raw_string_lit | interpreted_string_lit .
raw_string_lit         = "`" { unicode_char | newline } "`" .
interpreted_string_lit = `"` { unicode_value | byte_value } `"` .

Примеры:

`abc`                // то же самое что и "abc"
`\n
\n`                  // то же самое что и "\\n\n\\n"
"\n"
"\""                 // то же самое что и `"`
"Hello, world!\n"
"日本語"
"\u65e5本\U00008a9e"
"\xff\u00FF"
"\uD800"             // недопустимо: суррогатная половина (surrogate half)
"\U00110000"         // недопустимо: невалидная Unicode кодовая точка

Следующие примеры представляют одну и ту же строку:

"日本語"                                 // UTF-8 текст ввода
`日本語`                                 // UTF-8 текст ввода как необработанный литерал
"\u65e5\u672c\u8a9e"                    // явные Unicode кодовые точки
"\U000065e5\U0000672c\U00008a9e"        // явные Unicode кодовые точки
"\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e"  // явные UTF-8 байты

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


Читайте также:


Спецификация Go: литералы рун

Литералы рун (Rune literals)

Литерал руны представляет собой константу руны, целочисленное значение, идентифицирующее кодовую точку Unicode. Литерал руны выражается в виде одного или нескольких символов, заключенных в одинарные кавычки, например, 'x' или '\n'. В кавычках может появляться любой символ, кроме новой строки и неэкранированной одинарной кавычки. Символ в кавычках представляет значение Unicode самого символа, а многосимвольные последовательности, начинающиеся с обратной косой черты, кодируют значения в различных форматах.

Самая простая форма представляет один символ в кавычках; поскольку исходный текст Go представляет собой символы Unicode, закодированные в UTF-8, несколько байтов в кодировке UTF-8 могут представлять одно целочисленное значение. Например, литерал 'a' содержит один байт, представляющий литерал a, Unicode U+0061, значение 0x61, в то время как 'ä' содержит два байта (0xc3 0xa4), представляющий литерал a-dieresis, U+00E4, значение 0xe4.

Несколько экранов с обратной косой чертой позволяют кодировать произвольные значения в виде текста ASCII. Существует четыре способа представления целочисленного значения в виде числовой константы: \x, за которой следуют ровно две шестнадцатеричные цифры; \u, за которой следуют ровно четыре шестнадцатеричные цифры; \U, за которой следуют ровно восемь шестнадцатеричных цифр и простой обратный слеш \, за которыми следуют ровно три восьмеричных знака. В каждом случае значение литерала - это значение, представленное цифрами в соответствующей базе.

Хотя все эти представления приводят к целому числу, они имеют разные допустимые диапазоны. Восьмеричные экранирования должны представлять значение от 0 до 255 включительно. Шестнадцатеричные экранирования удовлетворяют этому условию по построению. Экранирующие символы \u и \U представляют кодовые точки Unicode, поэтому в них некоторые значения недопустимы, в частности значения выше 0x10FFFF и суррогатные половины.

После обратной косой черты некоторые односимвольные экранированные символы представляют собой специальные значения:

\a   U+0007 alert or bell
\b   U+0008 backspace (пробел)
\f   U+000C form feed (символ конца страницы)
\n   U+000A line feed or newline (символ возврата строки)
\r   U+000D carriage return (символ возврата каретки)
\t   U+0009 horizontal tab (горизонтальный таб)
\v   U+000b vertical tab (вертикальный таб)
\\   U+005c backslash (обратный слеш)
\'   U+0027 single quote  (одинарная кавычка, валидный экран только внутри литералов рун)
\"   U+0022 double quote  (двойная кавычка, валидный экран только внутри строковых литералов)

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

rune_lit         = "'" ( unicode_value | byte_value ) "'" .
unicode_value    = unicode_char | little_u_value | big_u_value | escaped_char .
byte_value       = octal_byte_value | hex_byte_value .
octal_byte_value = `\` octal_digit octal_digit octal_digit .
hex_byte_value   = `\` "x" hex_digit hex_digit .
little_u_value   = `\` "u" hex_digit hex_digit hex_digit hex_digit .
big_u_value      = `\` "U" hex_digit hex_digit hex_digit hex_digit
                           hex_digit hex_digit hex_digit hex_digit .
escaped_char     = `\` ( "a" | "b" | "f" | "n" | "r" | "t" | "v" | `\` | "'" | `"` ) .

Примеры:

'a'
'ä'
'本'
'\t'
'\000'
'\007'
'\377'
'\x07'
'\xff'
'\u12e4'
'\U00101234'
'\''         // литерал руны, содуржащий символ одинарной кавычки
'aa'         // недопустимо: слишком много символов
'\xa'        // недопустимо: слишком мало шестнадцатеричных цифр
'\0'         // недопустимо: слишком мало восьмеричных цифр
'\uDFFF'     // недопустимо: суррогатная половина
'\U00110000' // недопустимо: невалидная Unicode кодовая точка


Читайте также:


Спецификация Go: литералы

Целочисленные литералы (Integer literals)

Целочисленный литерал - это последовательность цифр, представляющая целочисленную константу. Необязательный префикс устанавливает недесятичное основание: 0 для восьмеричного, 0x или 0X для шестнадцатеричного. В шестнадцатеричных литералах буквы a-f и A-F представляют значения от 10 до 15.

int_lit     = decimal_lit | octal_lit | hex_lit .
decimal_lit = ( "1" … "9" ) { decimal_digit } .
octal_lit   = "0" { octal_digit } .
hex_lit     = "0" ( "x" | "X" ) hex_digit { hex_digit } .

Примеры:

42
0600
0xBadFace
170141183460469231731687303715884105727

Литералы с плавающей точкой (Floating-point literals)

Литерал с плавающей точкой является десятичным представлением константы с плавающей точкой. Он имеет целочисленную часть, десятичную точку, дробную часть и экспоненту. Целая и дробная части содержат десятичные цифры; часть экспоненты представляет собой e или E, за которыми следует десятичный показатель степени с необязательной подписью. Одна из целочисленной части или дробной части может быть исключена; одна из десятичной точки или показатель степени могут быть исключены.

float_lit = decimals "." [ decimals ] [ exponent ] |
            decimals exponent |
            "." decimals [ exponent ] .
decimals  = decimal_digit { decimal_digit } .
exponent  = ( "e" | "E" ) [ "+" | "-" ] decimals .

Примеры:

0.
72.40
072.40  // == 72.40
2.71828
1.e+0
6.67428e-11
1E6
.25
.12345E+5

Мнимые литералы (Imaginary literals)

Мнимый литерал - это десятичное представление мнимой части комплексной константы. Он состоит из литерального или десятичного целого числа с плавающей точкой, за которым следует строчная буква i.

imaginary_lit = (decimals | float_lit) "i" .

Примеры:

0i
011i  // == 11i
0.i
2.71828i
1.e+0i
6.67428e-11i
1E6i
.25i
.12345E+5i

Примечание: комплексное число (что такое мнимая часть, Википедия)


Читайте также:


воскресенье, 19 мая 2019 г.

Спецификация Go: лексические элементы

Комментарии

Комментарии служат программной документацией. Есть две формы комментариев в Go:

  • Строчные комментарии (Line comments) начинаются с последовательности символов // и заканчиваются в конце строки.

  • Общие комментарии (General comments) начинаются с последовательности символов /* и заканчиваются первой встреченной следом последовательностью символов */.

Комментарий не может начинаться внутри рунного или строкового литерала или внутри комментария. Общий комментарий (/* */), не содержащий новых строк, действует как пробел. Любой другой комментарий действует как перевод строки.

Токены (Tokens)

Токены образуют словарь языка Go. Существует четыре класса токенов: идентификаторы (identifier), ключевые слова (keyword), операторы (operator) и знаки препинания (punctuation), литералы (literal). Пустое место, образованное из пробелов (U+0020), горизонтальных табуляций (U+0009), возвратов каретки (U+000D) и символа перевода строки (newline) (U+000A), игнорируется, за исключением того, что оно разделяет токены, которые в противном случае объединялись бы в один токен. Кроме того, новая строка (newline) или конец файла может инициировать вставку точки с запятой. Во время разделения входных данных на токены, следующим отдельным токеном считается самая длинная последовательность символов, которые образуют валидный токен.

Точки с запятой

Формальная грамматика использует точки с запятой ";" как терминаторы в ряде производств. Go программы могут опустить большинство этих точек с запятой, используя следующие два правила:

  1. Когда входные данные разбиты на токены, точка с запятой автоматически вставляется в поток токенов сразу после последнего токена строки, если этот токен
    • идентификатор
    • целое число (integer), число с плавающей точкой (floating-point), мнимый (imaginary), рунический (rune) или строковый (string) литерал
    • одно из ключевых слов: break, continue, fallthrough, или return
    • один из операторов и знаков препинания ++, -, ), ] или }
  2. Чтобы разрешить сложным операторам занимать одну строку, точка с запятой может быть опущена перед закрывающими ")" или "}".

Идентификаторы

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

identifier = letter { letter | unicode_digit } .

a
_x9
ThisVariableIsExported
αβ

Некоторые идентификаторы предопределены.

Ключевые слова

Следующие ключевые слова зарезервированы и не могут быть использованы в качестве идентификаторов:

break        default      func         interface    select
case         defer        go           map          struct
chan         else         goto         package      switch
const        fallthrough  if           range        type
continue     for          import       return       var

Операторы и знаки пунктуации

Следующие последовательности символов представляют операторы (включая операторы присвоения) и знаки пунктуации:

+    &     +=    &=     &&    ==    !=    (    )
-    |     -=    |=     ||    <     <=    [    ]
*    ^     *=    ^=     <-    >     >=    {    }
/    <<    /=    <<=    ++    =     :=    ,    ;
%    >>    %=    >>=    --    !     ...   .    :
     &^          &^=


Читайте также:


суббота, 4 мая 2019 г.

Спецификация Go: представление исходного кода

Исходный код в Go - это Unicode текст, в кодировке UTF-8. Текст не канонизирован, поэтому одна акцентированная кодовая точка отличается от того же символа, сконструированного из сочетания ударения и буквы; они рассматриваются как две кодовые точки. Для простоты в спецификации используется неквалифицированный термин-символ для ссылки на Unicode кодовую точку в исходном тексте.

Каждая кодовая точка отличается; например, заглавные и строчные буквы - это разные символы.

Ограничение реализации: для совместимости с другими инструментами компилятор может запретить символ NUL (U+0000) в исходном тексте.

Ограничение реализации: для совместимости с другими инструментами компилятор может игнорировать метку порядка байтов в кодировке UTF-8 (U+FEFF), если это первая Unicode кодовая точка в исходном тексте. Метка порядка следования байтов может быть запрещена где-либо еще в источнике.

Символы

Следующие термины используются для обозначения определенных классов Unicode символов:

newline        = /* Unicode кодовая точка U+000A */ .
unicode_char   = /* произвольная Unicode кодовая точка исключая newline */ .
unicode_letter = /* Unicode кодовая точка классифицированная как "Letter"(Буква) */ .
unicode_digit  = /* Unicode кодовая точка классифицированная как "Number, decimal digit"(Число, десятичная цифра) */ .

В Unicode стандарте 8.0, Раздел 4.5 "General Category" определяет набор категорий символов. Go рассматривает все символы в любой из Letter категорий Lu, Ll, Lt, Lm, или Lo как Unicode буквы, а все символы в Number категории Nd как Unicode цифры.

Буквы и цифры

Символ нижнего подчеркивания _ (U+005F) считается буквой.

letter        = unicode_letter | "_" .
decimal_digit = "0" … "9" .
octal_digit   = "0" … "7" .
hex_digit     = "0" … "9" | "A" … "F" | "a" … "f" .


Читайте также: