Показаны сообщения с ярлыком преобразования типов в Go. Показать все сообщения
Показаны сообщения с ярлыком преобразования типов в Go. Показать все сообщения

среда, 22 апреля 2020 г.

Преобразования в Golang

Выражение T(x) преобразует значение x в тип T.

x := 5.1
n := int(x) // преобразует float в int

Правила преобразования обширны, но предсказуемы:

  • все преобразования между типизированными выражениями должны быть явно указаны,
  • незаконные преобразования обнаруживаются компилятором.

Преобразование в и из чисел и строк может изменить представление и иметь стоимость во время выполнения. Все остальные преобразования изменяют только тип, но не представление x.

Интерфейсы

Чтобы "преобразовать" интерфейс в строку, структуру или карту (map), вы должны использовать утверждение типа или переключатель типа. Утверждение типа в действительности не преобразует интерфейс в другой тип данных, но обеспечивает доступ к конкретному значению интерфейса, которое обычно является тем, что вам нужно.

Целые числа

  • При преобразовании в более короткий целочисленный тип значение усекается, чтобы соответствовать размеру типа результата.
  • При преобразовании в более длинный целочисленный тип,
    • если значение является целым числом со знаком, оно расширяется знаком;
    • в противном случае оно расширяется нулями.

a := uint16(0x10fe) // 0001 0000 1111 1110
b := int8(a)        //           1111 1110 (усечено к -2)
c := uint16(b)      // 1111 1111 1111 1110 (расширено знаком к 0xfffe)

Числа с плавающей запятой

  • При преобразовании числа с плавающей запятой в целое число дробь отбрасывается (усечение до нуля).
  • При преобразовании целого числа или числа с плавающей запятой в ​​тип с плавающей запятой результирующее значение округляется до точности, указанной типом назначения.

var x float64 = 1.9
n := int64(x) // 1
n = int64(-x) // -1

n = 1234567890
y := float32(n) // 1.234568e+09

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

Целое число в строку

  • При преобразовании целого числа в строку значение интерпретируется как кодовая точка Unicode, и результирующая строка будет содержать символ, представленный этой кодовой точкой, закодированный в UTF-8.
  • Если значение не представляет допустимую кодовую точку (например, если оно отрицательное), результатом будет "\ufffd", символ замены Unicode �.

string(97) // "a"
string(-1) // "\ufffd" == "\xef\xbf\xbd"

Используйте strconv.Itoa, чтобы получить десятичное строковое представление целого числа.

strconv.Itoa(97) // "97"

Строки и срезы байтов

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

string([]byte{97, 230, 151, 165}) // "a日"
[]byte("a日")                     // []byte{97, 230, 151, 165}

Строки и срезы рун

  • Преобразование среза рун в строковый тип дает строку, которая является объединением отдельных значений рун, преобразованных в строки.
  • Преобразование значения типа строки в тип среза рун дает срез, содержащий отдельные кодовые точки Unicode строки.

string([]rune{97, 26085}) // "a日"
[]rune("a日")             // []rune{97, 26085}

Подлежащий тип

Неконстантное значение может быть преобразовано в тип T, если оно имеет тот же подлежащий тип, что и T.

В этом примере базовый тип int64, T1 и T2 - int64.

type (
 T1 int64
 T2 T1
)

В Go идиоматично преобразовывать тип выражения для доступа к определенному методу.

var n int64 = 12345
fmt.Println(n)                // 12345
fmt.Println(time.Duration(n)) // 12.345µs

Подлежащий тип time.Duration - int64, а тип time.Duration имеет метод String, который возвращает продолжительность (duration), отформатированную как время.

Неявные преобразования

Единственное неявное преобразование в Go - это когда нетипизированная константа используется в ситуации, когда требуется тип.

В этом примере нетипизированные литералы 1 и 2 неявно преобразуются.

var x float64
x = 1 // То же, что и x = float64(1)

t := 2 * time.Second 
// То же, что и t := time.Duration(2) * time.Second

Неявные преобразования необходимы, поскольку в Go нет смешивания числовых типов. Вы можете умножить time.Duration только на другое time.Duration.

Когда тип не может быть выведен из контекста, нетипизированная константа преобразуется в bool, int, float64, complex128, string или rune в зависимости от синтаксического формата константы.

n := 1   // То же, что и n := int(1)
x := 1.0 // То же, что и x := float64(1.0)
s := "A" // То же, что и s := string("A")
c := 'A' // То же, что и c := rune('A')

Незаконные неявные преобразования перехватываются компилятором.

var b byte = 256 // То же, что и var b byte = byte(256)

../main.go:2:6: constant 256 overflows byte

Указатели

Компилятор Go не допускает преобразования между указателями и целыми числами. Пакет unsafe реализует эту функцию в ограниченных условиях.

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


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


четверг, 13 июня 2019 г.

Спецификация Go: преобразования в и из строкового типа

1. Преобразование целочисленного значения со знаком или без знака в строковый тип дает строку, содержащую представление целого числа в UTF-8. Значения за пределами диапазона допустимых кодовых точек Unicode преобразуются в "\uFFFD".

string('a')       // "a"
string(-1)        // "\ufffd" == "\xef\xbf\xbd"
string(0xf8)      // "\u00f8" == "ø" == "\xc3\xb8"
type MyString string
MyString(0x65e5)  // "\u65e5" == "日" == "\xe6\x97\xa5"

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

string([]byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'})   // "hellø"
string([]byte{})                                     // ""
string([]byte(nil))                                  // ""

type MyBytes []byte
string(MyBytes{'h', 'e', 'l', 'l', '\xc3', '\xb8'})  // "hellø"

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

string([]rune{0x767d, 0x9d6c, 0x7fd4})   // "\u767d\u9d6c\u7fd4" == "白鵬翔"
string([]rune{})                         // ""
string([]rune(nil))                      // ""

type MyRunes []rune
string(MyRunes{0x767d, 0x9d6c, 0x7fd4})  // "\u767d\u9d6c\u7fd4" == "白鵬翔"

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

[]byte("hellø")   // []byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'}
[]byte("")        // []byte{}

MyBytes("hellø")  // []byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'}

5. Преобразование значения типа строки в срез типа руны дает срез, содержащий отдельные кодовые точки Unicode строки.

[]rune(MyString("白鵬翔"))  // []rune{0x767d, 0x9d6c, 0x7fd4}
[]rune("")                 // []rune{}

MyRunes("白鵬翔")           // []rune{0x767d, 0x9d6c, 0x7fd4}


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


Спецификация Go: преобразования между числовыми типами

Для преобразования непостоянных (non-constant) числовых значений применяются следующие правила:

  1. При преобразовании между целочисленными типами, если значение является целым числом со знаком, оно расширяется до неявной бесконечной точности; в противном случае это ноль продленный (zero extended). Затем он усекается, чтобы соответствовать размеру типа результата. Например, если v := uint16(0x10F0), то uint32(int8 (v)) == 0xFFFFFFF0. Преобразование всегда дает действительное значение; нет признаков переполнения.
  2. При преобразовании числа с плавающей точкой в целое число дробь отбрасывается (усечение до нуля).
  3. При преобразовании целого числа или числа с плавающей точкой в тип с плавающей точкой или комплексного числа в другой сложный тип значение результата округляется с точностью, указанной типом назначения. Например, значение переменной x типа float32 может быть сохранено с использованием дополнительной точности, превышающей точность 32-разрядного числа IEEE-754, но float32(x) представляет результат округления значения x до 32-разрядной точности. Аналогично, x + 0.1 может использовать точность более 32 бит, а float32(x + 0.1) - нет.

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


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


Спецификация Go: преобразования

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

Явное преобразование - это выражение в форме T(x), где T - это тип, а x - это выражение, которое можно преобразовать в тип T.

Conversion = Type "(" Expression [ "," ] ")" .

Если тип начинается с оператора * или <-, или если тип начинается с ключевого слова func и не имеет списка результатов, его необходимо заключить в скобки когда необходимо, чтобы избежать двусмысленности:

*Point(p)        // тоже что и *(Point(p))
(*Point)(p)      // p преобразовано в *Point
<-chan int(c)    // тоже что и <-(chan int(c))
(<-chan int)(c)  // c преобразовано в <-chan int
func()(x)        // сигнатура функции func() x
(func())(x)      // x преобразовано в func()
(func() int)(x)  // x преобразовано в func() int
func() int(x)    // x преобразовано в func() int (однозначно)

Постоянное значение x может быть преобразовано в тип T, если x представимо значением T. В особом случае целочисленная константа x может быть явно преобразована в строковый тип, используя то же правило, что и для непостоянной переменной x.

Преобразование константы дает типизированную константу как результат.

uint(iota)               // iota значение типа uint
float32(2.718281828)     // 2.718281828 типа float32
complex128(1)            // 1.0 + 0.0i типа complex128
float32(0.49999999)      // 0.5 типа float32
float64(-1e-1000)        // 0.0 типа float64
string('x')              // "x" типа string
string(0x266c)           // "♬" типа string
MyString("foo" + "bar")  // "foobar" типа MyString
string([]byte{'a'})      // не константа: []byte{'a'} это не константа
(*int)(nil)              // не константа: nil это не константа, *int это не boolean, numeric, или string тип
int(1.2)                 // недопустимо: 1.2 не может быть представлено как int
string(65.0)             // недопустимо: 65.0 iэто не integer константа

Непостоянное (non-constant) значение x может быть преобразовано в тип T в любом из следующих случаев:

  • х присваиваемо (assignable) Т.
  • игнорируя теги struct, тип x и T имеют идентичные базовые типы.
  • игнорируя теги struct, тип x и T являются типами указателей, которые не являются определенными типами, и их базовые типы указателей имеют идентичные базовые типы.
  • Тип x и T оба являются целочисленными или числами с плавающей точкой.
  • Тип x и T оба являются сложными типами.
  • x - целое число или срез байтов или рун, а T - строковый тип.
  • x - это строка, а T - срез байтов или рун.

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

type Person struct {
  Name    string
  Address *struct {
    Street string
    City   string
  }
}

var data *struct {
  Name    string `json:"name"`
  Address *struct {
    Street string `json:"street"`
    City   string `json:"city"`
  } `json:"address"`
}

// игнорируя теги, базовые типы идентичны
var person = (*Person)(data)  

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

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


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