Go никогда не преобразует типы неявно. Между совместимыми типами вы преобразуете через T(v), а между числами и текстом через пакет strconv. Два самых популярных в поиске преобразования:
strconv.Itoa («integer to ASCII») форматирует int как десятичный текст. strconv.Atoi («ASCII to integer») разбирает текст в int и возвращает ошибку, если текст не является корректным числом. Всегда проверяйте эту ошибку.
Преобразование T(v)
Синтаксис преобразования в Go похож на вызов функции с именем целевого типа. Он работает в трёх случаях:
- между числовыми типами (
int,int64,float64,uint8, ...), - между
stringи[]byteили[]rune, - между типами с общим базовым типом (
Celsiusиfloat64).
Посмотрите на результаты внимательно:
int(3.9)равно3, аint(-3.9)равно-3. Преобразование отбрасывает дробную часть в сторону нуля, а не округляет. Чтобы выбрать поведение, сначала применитеmath.Round,math.Floorилиmath.Ceil.math.Roundокругляет половину от нуля, поэтому-3.5становится-4.uint8(300)равно44, потому что остаются только младшие 8 бит (300 минус 256), аint8(200)по той же причине равно-56. Ни ошибки, ни паники.- У
Celsiusиfloat64общий базовый тип, поэтому они свободно преобразуются друг в друга, но только явно.
Значения намеренно лежат в переменных. С константами те же преобразования не скомпилируются.
Константы проверяются, переменные нет
Преобразование константы, которая не помещается в тип, это ошибка компиляции. Преобразование переменной с тем же значением молча заворачивается:
fmt.Println(uint8(300)) // compile error
x := 300
fmt.Println(uint8(x)) // 44
./main.go:6:20: constant 300 overflows uint8
Преобразование нецелой константы в целочисленный тип тоже отвергается, а то же значение в переменной просто усекается:
./main.go:7:18: cannot convert -3.9 (untyped float constant) to type int
Если значение float выходит за диапазон целевого целочисленного типа (скажем, int64(1e20)), результат зависит от реализации: например, amd64 и arm64 дают разные ответы. Проверяйте диапазон, прежде чем преобразовывать непроверенные значения.
Преобразование чисел в строки
| Из | Функция | Пример | Результат |
|---|---|---|---|
int | strconv.Itoa(n) | strconv.Itoa(-7) | "-7" |
int64 | strconv.FormatInt(n, base) | strconv.FormatInt(255, 16) | "ff" |
uint64 | strconv.FormatUint(n, base) | strconv.FormatUint(5, 2) | "101" |
float64 | strconv.FormatFloat(f, fmt, prec, 64) | strconv.FormatFloat(3.14159, 'f', 2, 64) | "3.14" |
bool | strconv.FormatBool(b) | strconv.FormatBool(true) | "true" |
| что угодно | fmt.Sprint(v) | fmt.Sprint(3.5) | "3.5" |
| что угодно, с форматом | fmt.Sprintf(format, v) | fmt.Sprintf("%05d", 42) | "00042" |
FormatFloat принимает байт формата ('f' для фиксированной записи, 'e' для экспоненциальной, 'g' для той, что короче), точность (-1 означает «наименьшее число цифр, которое точно восстанавливает значение») и разрядность исходного float (64 или 32).
strconv быстрее, чем fmt.Sprint, который упаковывает значение в интерфейс и проходит через общий путь форматирования fmt. В горячем цикле разница заметна; для редких преобразований выбирайте то, что лучше читается.
Преобразование строк в числа
| Во что | Функция | Возвращает |
|---|---|---|
int | strconv.Atoi(s) | int, error |
| любое знаковое целое | strconv.ParseInt(s, base, bitSize) | int64, error |
| любое беззнаковое целое | strconv.ParseUint(s, base, bitSize) | uint64, error |
| float | strconv.ParseFloat(s, bitSize) | float64, error |
| bool | strconv.ParseBool(s) | bool, error |
ParseInt всегда возвращает int64. Аргумент bitSize (0, 8, 16, 32, 64) задаёт допустимый диапазон, так что результат гарантированно поместится, когда вы преобразуете его в меньший тип. base 0 означает «определить по префиксу», поэтому разбираются 0x1f, 0o17, 0b101 и 1_000.
Что показывает вывод:
" 42"не разбирается.Atoiпринимает необязательный знак и цифры, больше ничего. Ввод изbufioобычно заканчивается на\n, поэтому обрезайте его."4.2"дляAtoiнекорректно. Для дробных чисел используйтеParseFloat.- При ошибке выхода за диапазон
ParseIntвозвращает наибольшее значение, которое помещается (127дляint8), вместе с ошибкой. Не используйте значение, если ошибка не nil. - Ошибки имеют тип
*strconv.NumErrorи оборачиваютstrconv.ErrSyntaxилиstrconv.ErrRange, поэтомуerrors.Isможет их различить.
ParseBool принимает 1, t, T, TRUE, true, True и соответствующие формы false. "yes" и "on" дают ошибку.
Ловушка string(65)
Преобразование целого числа в string не даёт цифры. Оно даёт символ с этой кодовой точкой Unicode:
n := 65
s := string(n) // "A", not "65"
Компилятор это пропускает, но go vet сообщает:
./main.go:10:7: conversion from int to string yields a string of one rune, not a string of digits
Если вам действительно нужен символ, скажите это явно через руну: string(rune(n)). Если нужно число в виде текста, используйте strconv.Itoa(n). Преобразовать rune или byte в string можно, и это обычное дело:
Строки, байты и руны
string, []byte и []rune преобразуются друг в друга через T(v). Каждое преобразование копирует данные, потому что строки неизменяемы, а слайсы нет:
Преобразуйте в []rune, когда нужно индексировать или изменять по символам; в []byte для ввода-вывода, хеширования и правок на уровне байтов. UTF-8 подробно разобран на странице о рунах и байтах.
Преобразование и утверждение типа
Преобразование меняет значение одного конкретного типа на другой. Достать конкретное значение из интерфейса (any, error, io.Reader) это другая операция, утверждение типа (type assertion):
var v any = 42
n := v.(int) // type assertion: v holds an int, give it to me
f := float64(n) // conversion: int to float64
// f := float64(v) // compile error: cannot convert v (variable of interface type any)
// // to type float64: need type assertion
Сначала утверждение, затем преобразование. Форма comma-ok n, ok := v.(int) позволяет избежать паники, если тип не тот.
Шпаргалка
| Задача | Код |
|---|---|
| int в string | strconv.Itoa(n) |
| int64 в string | strconv.FormatInt(n, 10) |
| string в int | n, err := strconv.Atoi(s) |
| string в int64 | n, err := strconv.ParseInt(s, 10, 64) |
| string в float64 | f, err := strconv.ParseFloat(s, 64) |
| float64 в string | strconv.FormatFloat(f, 'f', -1, 64) |
| float64 в int | int(f) (отбрасывает дробь) или int(math.Round(f)) |
| int в float64 | float64(n) |
| string в []byte | []byte(s) |
| []byte в string | string(b) |
| rune в string | string(r) |
| bool в string | strconv.FormatBool(b) |
| любое значение в string | fmt.Sprint(v) |
Часто задаваемые вопросы
Как преобразовать int в string в Go?
Используйте strconv.Itoa(n): strconv.Itoa(42) возвращает "42". Для int64 подойдёт strconv.FormatInt(n, 10). fmt.Sprint(n) тоже работает с любым типом, но медленнее. Не пишите string(n): так n трактуется как кодовая точка Unicode, и string(65) равно "A".
Как преобразовать string в int в Go?
Используйте strconv.Atoi, которая возвращает число и ошибку:
n, err := strconv.Atoi("42")
if err != nil {
// not a valid integer
}
Для конкретного размера или системы счисления используйте strconv.ParseInt(s, 10, 64), она возвращает int64. Atoi не обрезает пробелы, поэтому " 42" даёт ошибку; сначала вызовите strings.TrimSpace.
Как сделать приведение типов (cast) в Go?
В Go это называется преобразованием, синтаксис T(v): float64(n), int(f), []byte(s). Оно работает между числовыми типами, между строками и слайсами байтов или рун, а также между типами с одинаковым базовым типом. Разбирать текст оно не умеет: чтобы превратить "42" в 42, нужен strconv. Достать конкретный тип из интерфейса можно утверждением типа v.(T), а не преобразованием.
Как преобразовать float в int в Go?
int(f) отбрасывает дробную часть в сторону нуля: int(3.9) равно 3, а int(-3.9) равно -3. Для округления используйте int(math.Round(f)). Если float выходит за диапазон целочисленного типа, результат зависит от реализации, поэтому проверяйте диапазон, когда значение приходит из ввода.
Почему string(65) возвращает "A" в Go?
Преобразование целого числа в string трактует его как кодовую точку Unicode, а 65 это кодовая точка A. go vet это отмечает: conversion from int to string yields a string of one rune, not a string of digits. Чтобы получить "65", используйте strconv.Itoa(65).