Los tipos básicos de Go son números, booleanos y strings. Cada variable tiene exactamente un tipo, fijado en tiempo de compilación, y Go nunca convierte entre tipos por ti. %T imprime el tipo de cualquier valor:
Fíjate en que 'G' se imprime como 71 con tipo int32: un carácter entre comillas simples es un rune, que es un número (el code point Unicode). rune es un alias de int32 (el mismo tipo con otro nombre), así que %T muestra int32. Del mismo modo, byte muestra uint8.
Enteros
| Tipo | Tamaño | Rango |
|---|---|---|
int8 | 8 bits | -128 a 127 |
int16 | 16 bits | -32.768 a 32.767 |
int32 | 32 bits | unos -2.100 millones a 2.100 millones |
int64 | 64 bits | unos -9,2 trillones a 9,2 trillones |
uint8 (byte) | 8 bits | 0 a 255 |
uint16 | 16 bits | 0 a 65.535 |
uint32 | 32 bits | 0 a unos 4.300 millones |
uint64 | 64 bits | 0 a unos 18,4 trillones |
int, uint | 32 o 64 bits | tamaño de palabra de la plataforma |
uintptr | tamaño de la plataforma | contiene el valor de un puntero, para código de bajo nivel |
Usa int por defecto. len() devuelve int, los índices de los slices son int y los contadores de bucle son int. En todas las plataformas de 64 bits int tiene 64 bits. Recurre a los tipos con tamaño cuando el tamaño forma parte de un contrato: int32 en un formato de archivo binario, uint8 para bytes en bruto, int64 para una marca de tiempo Unix en nanosegundos.
Evita los tipos sin signo para cantidades que "no pueden ser negativas", como los recuentos. Restar por debajo de cero da la vuelta a un número enorme en lugar de producir uno negativo que podrías detectar.
Límites y desbordamiento
El paquete math tiene una constante para cada límite. En tiempo de ejecución, la aritmética entera que se desborda da la vuelta en silencio:
Ni panic ni error: 127 + 1 se convierte en -128. Solo las expresiones constantes se comprueban en tiempo de compilación (var b int8 = 128 no compila). Si en tus datos es posible un desbordamiento, compruébalo antes de la operación o usa math/big.
Literales enteros
million := 1_000_000 // underscores for readability (Go 1.13+)
mask := 0xFF // hexadecimal
perm := 0o755 // octal (also the older form 0755)
flags := 0b1010 // binary
Números de punto flotante
| Tipo | Tamaño | Precisión |
|---|---|---|
float32 | 32 bits | unos 6 a 9 dígitos significativos |
float64 | 64 bits | unos 15 a 17 dígitos significativos |
Un literal con punto decimal o exponente (2.5, 1e6) es float64 por defecto, y el paquete math recibe y devuelve float64. Úsalo salvo que importe la memoria de un array grande.
Los flotantes son binarios, así que la mayoría de las fracciones decimales son aproximaciones:
De ahí salen dos reglas. Nunca compares flotantes con == después de hacer aritmética. Y nunca guardes dinero en un flotante: usa un número entero de céntimos o una librería decimal.
Dividir un flotante entre cero da +Inf, -Inf o NaN en lugar de provocar un panic. Dividir un entero entre cero provoca un panic en tiempo de ejecución (runtime error: integer divide by zero), y un divisor cero constante es un error de compilación.
Booleanos
bool contiene true o false, y su valor cero es false. Go no trata los números, los strings vacíos ni nil como booleanos. if count {} es un error de compilación; escribe if count > 0 {}. Los operadores son &&, || y !, y && y || hacen cortocircuito.
Strings
Un string es una secuencia inmutable de bytes, normalmente texto UTF-8. len(s) cuenta bytes, no caracteres:
é ocupa dos bytes en UTF-8, así que len devuelve 6 para cinco caracteres. Los strings entre comillas dobles procesan escapes como \n; los raw strings entre comillas invertidas no, y pueden ocupar varias líneas. La página de strings cubre el paquete strings, y la de runes y bytes trata UTF-8 a fondo.
byte y rune
| Alias | Igual que | Contiene |
|---|---|---|
byte | uint8 | un byte de datos en bruto |
rune | int32 | un code point Unicode |
Son alias, no tipos nuevos: un byte y un uint8 son intercambiables sin conversión. Los nombres dicen qué significa el número.
Números complejos
Go tiene tipos complejos integrados, complex64 y complex128, con real, imag y el paquete math/cmplx:
Pocos programas los necesitan, pero el procesamiento de señales y algo de código numérico sí.
Tipos compuestos y de referencia
Los tipos básicos se combinan en tipos compuestos. Cada uno tiene su propia página:
| Tipo | Ejemplo | Valor cero |
|---|---|---|
| array | [3]int | tres ceros |
| slice | []int | nil |
| map | map[string]int | nil |
| struct | struct{ X, Y int } | todos los campos a cero |
| puntero | *int | nil |
| función | func(int) int | nil |
| channel | chan int | nil |
| interfaz | error, any | nil |
any es un alias de interface{} (desde Go 1.18) y puede contener un valor de cualquier tipo.
Tipos con nombre
Puedes definir un tipo nuevo a partir de uno existente. Tiene la misma representación pero es un tipo distinto, y puede tener métodos:
type Celsius float64
type UserID int64
var t Celsius = 21.5
var f float64 = t // compile error: cannot use t (variable of float64 type Celsius) as float64 value in variable declaration
Esa rigidez es precisamente la idea: un UserID no se puede pasar por accidente donde se espera un OrderID. Convierte de forma explícita cuando sea lo que quieres: float64(t). Consulta la conversión de tipos para las reglas y las funciones de strconv para pasar entre números y strings.
Valores cero
Cada tipo tiene un valor cero, que es lo que contiene una variable antes de que le asignes nada: 0 para los números, false para bool, "" para los strings y nil para punteros, slices, maps, channels, funciones e interfaces. En Go no hay variables sin inicializar.
Preguntas frecuentes
¿Qué tamaño tiene un int en Go?
int y uint son de 64 bits en plataformas de 64 bits (amd64, arm64) y de 32 bits en plataformas de 32 bits. Si necesitas un tamaño fijo, para un formato de archivo o un protocolo de red, usa int32, int64, uint8, etc. Para contadores, índices y longitudes normales, usa int.
¿Cómo imprimo el tipo de una variable en Go?
Usa el verbo %T: fmt.Printf("%T\n", x) imprime int, float64, []string, main.User, etc. En código, reflect.TypeOf(x) devuelve la misma información como un valor que puedes inspeccionar.
¿Qué diferencia hay entre byte y rune en Go?
byte es un alias de uint8 y contiene un byte de datos en bruto. rune es un alias de int32 y contiene un code point Unicode. Un string es una secuencia de bytes; recorrerlo con for range lo decodifica en runes.
¿Debo usar float32 o float64 en Go?
Usa float64 salvo que tengas un motivo para no hacerlo. Es el tipo por defecto de los literales de punto flotante, el tipo con el que trabaja el paquete math, y tiene unos 15 a 17 dígitos decimales significativos de precisión frente a unos 6 a 9 de float32. Elige float32 solo para reducir a la mitad la memoria en arrays grandes o para coincidir con un formato externo.
¿Cuál es el valor máximo de int en Go?
Usa las constantes del paquete math: math.MaxInt (9223372036854775807 en plataformas de 64 bits), math.MinInt, math.MaxInt64, math.MaxUint32, etc. La aritmética que supera el límite da la vuelta en silencio en tiempo de ejecución; no provoca un panic.