Saiba mais sobre os tipos de dados básicos

Concluído

O Go é uma linguagem fortemente tipada. Cada variável declarada está associada a um tipo de dados específico e aceitará apenas os valores que correspondem àquele tipo.

No Go, há quatro categorias de tipos de dados:

  • Tipos básicos: números, cadeias de caracteres e boolianos
  • Tipos de agregação: matrizes e structs
  • Tipos de referência: ponteiros, fatias, mapas, funções e canais
  • Tipos de interface: interface

Neste módulo, estamos abordando apenas os tipos básicos. Não se preocupe se não souber quais são os outros tipos. Falaremos sobre eles nos próximos módulos.

Vamos começar explorando os tipos de dados numéricos.

Números inteiros

Em termos gerais, a palavra-chave usada para definir um tipo inteiro é int. Mas o Go também fornece os tipos int8, int16, int32 e int64, que são ints com um tamanho igual a 8, 16, 32 ou 64 bits, respectivamente. Ao usar um sistema operacional de 32 bits, caso use somente o int, o tamanho geralmente é de 32 bits. Em sistemas de 64 bits, o tamanho de int geralmente é de 64 bits. Contudo, esse comportamento pode variar de um computador para outro. É possível usar o uint. No entanto, somente use esse tipo caso precise declarar um valor como um número sem sinal por um determinado motivo. O Go também fornece os tipos uint8, uint16, uint32 e uint64.

Veja um exemplo de como usar os vários tipos inteiros no Go:

var integer8 int8 = 127
var integer16 int16 = 32767
var integer32 int32 = 2147483647
var integer64 int64 = 9223372036854775807
fmt.Println(integer8, integer16, integer32, integer64)

Na maioria das vezes, você usará int, mas precisará conhecer os outros tipos de inteiros porque, no Go, int não é o mesmo que int32, mesmo que o tamanho natural do inteiro seja 32 bits. Em outras palavras, você precisará converter o tipo explicitamente quando uma conversão for necessária. Se você tentar executar uma operação matemática entre tipos diferentes, receberá um erro. Por exemplo, suponha que você tenha este código:

var integer16 int16 = 127
var integer32 int32 = 32767
fmt.Println(integer16 + integer32)

Ao executar o programa, você receberá este erro:

invalid operation: integer16 + integer32 (mismatched types int16 and int32)

Como é possível observar, ao converter um valor de um tipo para outro na linguagem Go, será preciso declarar o novo tipo de modo explícito. Falaremos sobre como converter tipos corretamente no final deste módulo.

À medida que você avança no seu aprendizado, talvez ouça falar de runas. Um rune é simplesmente um alias para o tipo de dados int32. Ela é usada para representar um caractere Unicode (ou um ponto de código Unicode). Por exemplo, suponha que você tenha o seguinte código:

rune := 'G'
fmt.Println(rune)

Talvez você espere ver o programa imprimir G no prompt de comando ao executar o snippet de código anterior. Mas você vê o número 71, que representa o caractere Unicode de G. Falaremos mais sobre as runas nos próximos módulos.

Saiba sobre os intervalos de cada tipo examinando o código-fonte do Go. Conhecer os intervalos de cada tipo ajudará você a escolher o tipo de dados apropriado, e você também evitará o desperdício de bits na memória.

Desafio 1

definir outra variável do tipo int e usar o valor da variável integer32 ou integer64 para confirmar o tamanho natural da variável no sistema. Se você estiver em um sistema de 32 bits e usar um valor maior que 2.147.483.647, verá um erro de estouro semelhante a este: constant 9223372036854775807 overflows int.

Solução do desafio:

package main

import "fmt"

func main() {
   var integer32 int = 2147483648
   fmt.Println(integer32)
}

Desafio 2

declarar uma variável sem sinal como uint e inicializá-la com um valor negativo como -10. Ao tentar executar o programa, você receberá um erro como este: constant -10 overflows uint.

Solução do desafio:

package main

import "fmt"

func main() {
   var integer uint = -10
   fmt.Println(integer)
}

Números de ponto flutuante

O Go fornece tipos de dados para dois tamanhos de números de ponto flutuante: float32 e float64. Use esses tipos quando precisar armazenar números grandes e eles não couberem em nenhum dos tipos inteiros mencionados anteriormente. A diferença entre esses dois tipos é o tamanho máximo de bits que eles podem conter. Examine as seguintes linhas para saber como usar esses dois tipos:

var float32 float32 = 2147483647
var float64 float64 = 9223372036854775807
fmt.Println(float32, float64)

Encontre os limites desses dois tipos usando as constantes math.MaxFloat32 e math.MaxFloat64, que estão disponíveis no pacote math. Use o seguinte código para imprimir os valores máximos de ponto flutuante no prompt de comando:

package main

import (
    "fmt"
    "math"
)    

func main() {
    fmt.Println(math.MaxFloat32, math.MaxFloat64)
}

Os tipos de ponto flutuante também são úteis quando é necessário usar números decimais. Por exemplo, você poderá escrever algo como este código:

const e = 2.71828
const Avogadro = 6.02214129e23
const Planck = 6.62606957e-34

Observe que, com o código anterior, o Go inferirá os tipos de dados com base nos valores usados.

Boolianos

Um tipo booliano só tem dois valores possíveis: true e false. Declare um tipo booliano usando a palavra-chave bool. O Go é diferente de outras linguagens de programação. No Go, você não pode converter implicitamente um tipo booliano em 0 ou 1. Você precisará fazer isso explicitamente.

Portanto, você poderá declarar uma variável booliana desta forma:

var featureFlag bool = true

Usaremos tipos de dados boolianos no próximo módulo quando falarmos sobre as instruções de fluxo de controle no Go. Também os usaremos nos módulos posteriores.

Cadeias de caracteres

Por fim, vamos examinar o tipo de dados mais comum em qualquer linguagem de programação: cadeia de caracteres. No Go, a palavra-chave string é usada para representar um tipo de dados String. Para inicializar uma variável de cadeia de caracteres, você precisará definir o valor dela entre aspas duplas ("). Aspas simples (') são usadas para caracteres únicos (e para runas, como vimos em uma seção anterior).

Por exemplo, o seguinte código mostra duas maneiras de declarar e inicializar uma variável de cadeia de caracteres:

var firstName string = "John"
lastName := "Doe"
fmt.Println(firstName, lastName)

Às vezes, você precisará fazer escape dos caracteres. Para fazer isso no Go, use uma barra invertida (\) antes do caractere. Por exemplo, estes são os exemplos mais comuns de uso de caracteres de escape:

  • \n para novas linhas
  • \r para retornos de carro
  • \t para tabulações
  • \' para aspas simples
  • \" para aspas duplas
  • \\ para barras invertidas

Use o seguinte snippet de código para testar os caracteres de escape:

fullName := "John Doe \t(alias \"Foo\")\n"
fmt.Println(fullName)

Você verá a seguinte saída (incluindo a nova linha):

John Doe        (alias "Foo")

Valores padrão

Até agora, quase sempre que declaramos uma variável, inicializamos essa variável com um valor. Mas, no Go, ao contrário de outras linguagens de programação, todos os tipos de dados têm um valor padrão quando uma variável não é inicializada. Esse recurso é útil porque você não precisa verificar se uma variável foi inicializada antes de usá-la.

Veja uma lista de alguns valores padrão dos tipos que exploramos até agora:

  • 0 para os tipos int (e todos os respectivos subtipos, como int64)
  • +0.000000e+000 para os tipos float32 e float64
  • false para os tipos bool
  • Um valor vazio para os tipos string

Execute o seguinte snippet de código para confirmar os valores padrão listados anteriormente:

var defaultInt int
var defaultFloat32 float32
var defaultFloat64 float64
var defaultBool bool
var defaultString string
fmt.Println(defaultInt, defaultFloat32, defaultFloat64, defaultBool, defaultString)

Use um código como esse para determinar o valor padrão de um tipo de dados que não exploramos aqui.

Conversões de tipo

Em uma seção anterior, confirmamos que a conversão implícita não funciona no Go. No Go, a conversão precisa ser feita explicitamente. A linguagem oferece algumas maneiras nativas de converter um tipo de dados para outro. Por exemplo, uma forma de fazer isso é usar a função interna para cada tipo, desta maneira:

var integer16 int16 = 127
var integer32 int32 = 32767
fmt.Println(int32(integer16) + integer32)

Outra abordagem para a conversão no Go é usar o pacote strconv. Por exemplo, para converter uma string em um int e vice-versa, use este código:

package main

import (
    "fmt"
    "strconv"
)

func main() {
    i, _ := strconv.Atoi("-42")
    s := strconv.Itoa(-42)
    fmt.Println(i, s)
}

Execute o código anterior e confirme se ele é executado e imprime -42 duas vezes.

Observe que há um sublinhado (_) usado como o nome de uma variável no código anterior. No Go, _ significa que não usaremos o valor dessa variável e que desejamos ignorá-lo. Caso contrário, o programa não será compilado, porque precisamos usar todas as variáveis que declaramos. Voltaremos a este assunto e você aprenderá o que o _ normalmente representa nos próximos módulos.