Tipos básicos em Go

Tipos básicos em Go

Na lição passada, você aprendeu a criar variáveis usando os comandos var e const no GoLang.

Lá, você viu um pouco sobre alguns tipos básicos, como: string, int, float64 e boolean.

Nesta lição, você vai aprender tudo o que precisa saber sobre os tipos básicos, e como usá-los no seu dia a dia como desenvolvedor 🙂

Criando seu projeto de testes

Dentro da pasta JornadaGoLang, nós iremos criar uma nova pasta chamada de 6-tripos-basicos-em-go, onde dentro dela, vamos criar o nosso arquivo main.go:

package main

func main(){

}

Feito isso, vamos aprender um pouco mais sobre tipos básicos em GoLang!

O que são tipos básicos em Go?

No mundo real, nós temos categorias para tudo o que você possa imaginar. Existe a cor verde, amarela, vermelha, assim como também existem diversas taxonomias, como mamíferos, invertebrados, aves, anfíbios e etc...

No mundo da programação, nós também temos esses tipos de categorias, também chamados de tipos básicos, e que nos ajudam a identificar que tipo de valor uma variável irá armazenar.

Por vezes, você quer que uma variável armazene um texto, outras vezes, um decimal, algumas vezes um valor booleano (sim ou não) e por aí vai...

A boa é que a linguagem Go oferece um conjunto de tipos básicos que são usados para representar diferentes formas de dados.

Veremos abaixo os tipos básicos suportados pelo linguagem Go, começando pelos tipo de texto (string) 😇

String

Em programação, uma string é uma sequência de caracteres.

Esses caracteres podem ser letras, números, símbolos, espaços ou qualquer outro tipo de caractere que possa ser representado em um sistema de codificação, como o Unicode ou ASCII.

Uma string é formada por uma coleção ordenada de caracteres. Por exemplo, "Olá, Mundo!" é uma string que contém os caracteres:

'O', 'l', 'á', ',', ' ', 'M', 'u', 'n', 'd', 'o', e '!'.

Vejamos alguns exemplos do que são strings:

"Micilini"
"23847"
"#$¨%Y%$&I&Ö(K&%¨Y%GTRG$ÜIKO&*)(P~{}^`{^`´~´/´[]´[~'"

Percebeu que as strings estão sempre em volta de aspas duplas (")?

Esse é a forma correta de se criar strings em GoLang, strings são sempre definidas com aspas duplas ("):

var s string = "Olá, Mundo!"

É importante ressaltar que se você tiver um número, e o mesmo estiver sendo declarado dentro de aspas duplas, ele não é um considerado um número, mas sim uma string:

numero := "23"

Com o Go, você também pode criar uma string com múltiplas linhas usando o caractere ``, vejamos:

multiplasLinhas := `Olá
, Portal
da Micilini!`

Números Inteiros

Um outro tipo básico aceito pelo GoLang, e também pela maioria das linguagens de programação, são os números inteiros, vejamos uma lista completa de todos os tipos suportados por essa categoria:

  • int: O tipo padrão para inteiros, cujo tamanho depende da arquitetura do sistema (32 bits em sistemas de 32 bits e 64 bits em sistemas de 64 bits).
  • int8, int16, int32, int64: Inteiros com tamanho fixo em bits. O número após o int indica o número de bits.
  • uint: Inteiro sem sinal, com tamanho dependente da arquitetura do sistema.
  • uint8, uint16, uint32, uint64: Inteiros sem sinal de tamanho fixo.
  • byte: Um tipo que é sinônimo de uint8, normalmente usado para representar um valor de um byte (por exemplo, caracteres ASCII).
  • rune: Um tipo que é sinônimo de int32 e é utilizado para representar caracteres Unicode.

Para entendermos melhor o que foi dito acima, vamos dar uma olhada nas limitações de cada um deles:

Valores inteiros:
int: 42
int8: -128
int16: 32000
int32: 100000
int64: 9223372036854775807

Valores inteiros sem sinal:
uint: 42
uint8: 255
uint16: 65535
uint32: 400000
uint64: 18446744073709551615

Valores especiais:
byte (uint8): 65
rune (int32): 71

Vejamos agora, esses tipos básicos em ação:

package main

import "fmt"

func main() {
    // Números inteiros com sinal (com valores negativos e positivos)
    var i int = 42            // tipo int (tamanho depende da arquitetura do sistema, 32 ou 64 bits)
    var i8 int8 = -128        // tipo int8 (-128 a 127)
    var i16 int16 = 32000     // tipo int16 (-32.768 a 32.767)
    var i32 int32 = 100000    // tipo int32 (-2.147.483.648 a 2.147.483.647)
    var i64 int64 = 9223372036854775807 // tipo int64 (-9.223.372.036.854.775.808 a 9.223.372.036.854.775.807)

    // Números inteiros sem sinal (apenas valores positivos)
    var ui uint = 42          // tipo uint (tamanho depende da arquitetura do sistema)
    var ui8 uint8 = 255       // tipo uint8 (0 a 255)
    var ui16 uint16 = 65535   // tipo uint16 (0 a 65.535)
    var ui32 uint32 = 400000  // tipo uint32 (0 a 4.294.967.295)
    var ui64 uint64 = 18446744073709551615 // tipo uint64 (0 a 18.446.744.073.709.551.615)

    // Tipo byte (sinônimo de uint8)
    var b byte = 65           // tipo byte (valor entre 0 e 255, com 65 sendo o código ASCII para 'A')

    // Tipo rune (sinônimo de int32), usado para representar caracteres Unicode
    var r rune = 'G'          // tipo rune (representa o código Unicode de 'G', que é 71)

    // Exibindo os valores de cada variável
    fmt.Println("Valores inteiros com sinal:")
    fmt.Println("int:", i)
    fmt.Println("int8:", i8)
    fmt.Println("int16:", i16)
    fmt.Println("int32:", i32)
    fmt.Println("int64:", i64)

    fmt.Println("\nValores inteiros sem sinal:")
    fmt.Println("uint:", ui)
    fmt.Println("uint8:", ui8)
    fmt.Println("uint16:", ui16)
    fmt.Println("uint32:", ui32)
    fmt.Println("uint64:", ui64)

    fmt.Println("\nValores especiais:")
    fmt.Println("byte (uint8):", b) // 'A' (ASCII 65)
    fmt.Println("rune (int32):", r) // 71, que é o código Unicode de 'G'
}

Legal, não acha? 😉

Números de ponto flutuante

Se existem os números inteiros, é bem provável que o GoLang também nós dê suporte a "números quebrados", certo?

Sim, com o GoLang podemos usar dois tipos de números de ponto flutuante, que são usados para representar números com parte decimal, são eles:

  • float32: Números de ponto flutuante de precisão simples.
  • float64: Números de ponto flutuante de precisão dupla (é o tipo padrão para ponto flutuante).

No caso do float32 (flutuante de precisão simples), temos os seguintes limites:

  • Valor máximo: 3.4028235e+38
  • Valor mínimo: -3.4028235e+38

Já no caso do float64 (flutuante de precisão dupla), temos os seguintes limites:

  • Valor máximo: 1.7976931348623157e+308
  • Valor mínimo: -1.7976931348623157e+308

Vejamos agora, esses tipos básicos em ação:

package main

import "fmt"

func main() {
    // float32 - precisão simples
    var f32 float32 = 3.1415927  // Um número com precisão simples
    var f32Max float32 = 3.4028235e+38  // Valor máximo de float32
    var f32Min float32 = -3.4028235e+38 // Valor mínimo de float32

    // float64 - precisão dupla
    var f64 float64 = 3.141592653589793  // Um número com precisão dupla
    var f64Max float64 = 1.7976931348623157e+308 // Valor máximo de float64
    var f64Min float64 = -1.7976931348623157e+308 // Valor mínimo de float64

    // Exibindo os valores e limites
    fmt.Println("Exemplos de float32 e float64:")
    
    // Exemplo de float32
    fmt.Println("\nfloat32 (precisão simples):")
    fmt.Println("Valor de f32:", f32)
    fmt.Println("Valor máximo de float32:", f32Max)
    fmt.Println("Valor mínimo de float32:", f32Min)

    // Exemplo de float64
    fmt.Println("\nfloat64 (precisão dupla):")
    fmt.Println("Valor de f64:", f64)
    fmt.Println("Valor máximo de float64:", f64Max)
    fmt.Println("Valor mínimo de float64:", f64Min)
}

Números Complexos

No GoLang, nós temos um tipo básico que funciona de forma bem peculiar, chamado de números complexos.

Eles são usados para trabalhar com números que possuem uma parte real e uma parte imaginária, sendo úteis em várias áreas, como análise de sinais, engenharia elétrica, física e matemática, onde números complexos são frequentemente empregados.

Eles são representados por dois tipos principais:

complex64:

  • Representa um número complexo onde tanto a parte real quanto a parte imaginária têm 32 bits (ou seja, cada uma delas é do tipo float32).
  • Portanto, um número complexo do tipo complex64 tem precisão simples.

complex128:

  • Representa um número complexo onde tanto a parte real quanto a parte imaginária têm 64 bits (ou seja, cada uma delas é do tipo float64).
  • Portanto, um número complexo do tipo complex128 tem precisão dupla e maior capacidade de representar números.

Ambos os argumentos devem ser do tipo float32 ou float64 para complex64 ou complex128, respectivamente.

Vejamos um exemplo de seus usos:

package main

import "fmt"

func main() {
    // Definindo números complexos (com parte real e imaginária)
    
    // complex64: parte real e imaginária como float32 (precisão simples)
    var c64 complex64 = complex(3.5, 4.2)  // real: 3.5, imaginário: 4.2
    var c64Alt complex64 = complex(float32(1.5), float32(-2.5))  // outro exemplo de complex64
    
    // complex128: parte real e imaginária como float64 (precisão dupla)
    var c128 complex128 = complex(5.5, 6.7) // real: 5.5, imaginário: 6.7
    var c128Alt complex128 = complex(2.3, -1.4) // outro exemplo de complex128

    // Exibindo os valores dos números complexos
    fmt.Println("Exemplos de números complexos:")

    // Exibindo números complexos complex64
    fmt.Println("\ncomplex64 (precisão simples):")
    fmt.Println("c64:", c64)
    fmt.Println("c64Alt:", c64Alt)

    // Exibindo números complexos complex128
    fmt.Println("\ncomplex128 (precisão dupla):")
    fmt.Println("c128:", c128)
    fmt.Println("c128Alt:", c128Alt)

    // Acessando a parte real e a parte imaginária de números complexos
    fmt.Println("\nAcessando partes real e imaginária:")
    fmt.Println("Parte real de c128:", real(c128))
    fmt.Println("Parte imaginária de c128:", imag(c128))
}

Booleanos

Existem também aqueles tipos nas quais nós queremos armazenar apenas dois valores, SIM ou NÃO, VERDADEIRO ou FALSO, 0 ou 1.

E nada melhor como utilizar um booleano para isso 😉

O tipo bool é fundamental para controle de fluxo em programas, como em estruturas condicionais (if, switch) e laços de repetição (for) que veremos em lições futuras, portanto... aguarde 🙃

A declaração desse tipo é bem fácil, vejamos:

package main

import "fmt"

func main() {
    // Declaração explícita de variáveis booleanas
    var a bool = true
    var b bool = false
}

Reconhecendo tipos usando a biblioteca Reflect

No GoLang, você tem a sua disposição uma pacote chamado reflect, que é usado para para inspecionar tipos em tempo de execução.

Com ele, você conseguirá confirmar um tipo específico que uma determinada variável está armazenando, vejamos alguns exemplos:

package main

import (
	"fmt"
	"reflect"
)

func main(){
        fmt.Println(reflect.TypeOf(32000))
        fmt.Println(reflect.TypeOf("Olá mundo"))
        fmt.Println(reflect.TypeOf(true))

        nome := "Micilini Roll"
        fmt.Println(reflect.TypeOf(nome))
}

reflect.TypeOf(): Retorna o tipo completo do valor (int, float64, string, etc.).

package main

import (
	"fmt"
	"reflect"
)

func main(){
        fmt.Println(reflect.ValueOf(32000))
        fmt.Println(reflect.ValueOf("Olá mundo"))
        fmt.Println(reflect.ValueOf(true))

        nome := "Micilini Roll"
        fmt.Println(reflect.ValueOf(nome))
}

reflect.ValueOf(): Retorna um objeto reflect.Value, permitindo manipular o valor dinamicamente.

Tipos Zero

Em GoLang, nós temos os famosos tipos zeros, que nada mais são do que variáveis que armazenam nenhum tipo de valor inicial.

Como vimos na lição anterior, nós podemos ter as seguintes variáveis por meio do comando var:

package main

func main(){
        var nome string
        var idade int
        var abc float64 
        var booleano bool 
        //.....
}

Toda e qualquer variável que não possui um valor por padrão, é considerada do tipo zero.

Realizando conversões entre tipos básicos

Sim, quando você está criando tipos básicos em GoLang, é possível realizar conversões de forma explícita, por meio da seguinte sintaxe:

novoTipo(valor)

Vejamos alguns exemplos abaixo de conversão entre tipos básicos 

Conversão entre tipos numéricos

Para fazer conversão entre tipos numéricos em Go (inteiros, ponto flutuante e complexos), mas você precisa fazer isso de forma explicita, exemplo:

Supondo que você queira converter uma inteiro para inteiro:

var i int = 42
var smallInt int8 = int8(i)

Tenha em mente que conversões entre tipos inteiros (int, int8, int16, int32, int64, uint, etc.) podem causar truncamento se o valor for grande.

Já para inteiro para ponto flutuante, podemos usar:

var i int = 42
var f float64 = float64(i)

De ponto flutuante para inteiro:

var f float64 = 42.9
var i int = int(f) // Resultado: 42

var f32 float32 = 3.14
var f64 float64 = float64(f32)

De inteiro/flutuante para complex:

var realPart float64 = 3.0
c := complex(realPart, 4.5) // complex128

Conversão entre tipos de string

Quando temos uma string, e desejamos converter seu valor para qualquer outro tipo disponível, podemos fazer isso de algumas formas.

De string para byte slice:

s := "GoLang"
b := []byte(s)

De byte slice para string:

b := []byte{71, 111}
s := string(b) // "Go"

De string para rune slice:

s := "Gô"
r := []rune(s)

De rune slice para string:

r := []rune{'G', 'ô'}
s := string(r) // "Gô"

Conversão entre booleanos

Com relação as variáveis do tipo bool, no momento não há conversão implícita ou direta entre esses tipos. O que faz com que você tenha que validá-los manualmente.

Conversões avançadas entre tipos básico com Go

Anteriormente, você aprendeu a realizar conversões básicas entre tipos básicos. Mas será que é possível converter uma string para um inteiro? Ou quem sabe um complex para uma string?

Sim, isso é totalmente possível por meio de uma biblioteca padrão chamada de strconv 🙂

Para realizar uma conversão entre um tipo string para o tipo int, use a função strconv.Atoi() ou strconv.ParseInt() da seguinte forma:

package main

import (
	"fmt"
	"strconv"
)

func main() {
	s := "42"
	i, err := strconv.Atoi(s)
	if err != nil {
		fmt.Println("Erro na conversão:", err)
		return
	}
	fmt.Println(i) // Saída: 42
}

Com a função strconv.ParseInt() você consegue especificar a base e o tamanho, observe:

s := "42"
i, err := strconv.ParseInt(s, 10, 32) // Base 10, int32
if err != nil {
	fmt.Println("Erro:", err)
}
fmt.Println(i) // Saída: 42
  • strconv.Atoi() só funciona com números em base 10.
  • strconv.ParseInt() aceita diferentes bases (2, 8, 10, 16) e tamanhos (int8, int16, etc.).
  • Sempre trate o erro para evitar problemas com strings inválidas.

Para realizar uma conversão de um int para uma string, use strconv.Itoa() ou strconv.FormatInt():

package main

import (
	"fmt"
	"strconv"
)

func main() {
	i := 42
	s := strconv.Itoa(i)
	fmt.Println(s) // Saída: "42"
}

Já com a função strconv.FormatInt() podemos realizar a conversão para int64 em bases personalizadas, observe:

i := int64(42)
s := strconv.FormatInt(i, 10) // Base 10
fmt.Println(s) // Saída: "42"

s := strconv.FormatInt(255, 16) // "ff" (base 16)


Caso você queria fazer conversões entre booleanos, a biblioteca possuí a função strconv.ParseBool(), que pode ser usada para converter uma string em um valor booleano (true ou false).

package main

import (
	"fmt"
	"strconv"
)

func main() {
	val, err := strconv.ParseBool("true")
	if err != nil {
		fmt.Println("Erro na conversão:", err)
		return
	}
	fmt.Println("Valor booleano:", val) // Saída: true
}

E sim, dentro do exemplo acima, você deve ter notado algumas funções e operações que ainda não aprendemos em GoLang, mas fique tranquilo que veremos cada uma delas nas próximas aulas 😉

Para mais informações sobre o funcionamento da biblioteca strconv, consulte a documentação da mesma por meio deste link.

Repositório da lição

Todos os arquivos relacionados com esta lição, podem ser encontrados nos seguintes repositórios abaixo:

Conclusão

Para finalizarmos, vou colocar aqui um super resumo de tudo o que vimos até agora nesta lição

Números Inteiros: int, int8, int16, int32, int64, int, uint8, uint16, uint32, uint64, byte (sinônimo de uint8) e rune (sinônimo de int32).

Números com Ponto Flutuante: float32, float64 (tipo padrão).

Números Complexos: complex64, complex128.

Booleanos: bool.

Texto: string.

Esses são os tipos básicos que você pode utilizar em GoLang, e apesar dos exemplos estarem utilizando var, você também pode criá-los usando constantes (const).

Além deles, existem também os tipos compostos (como arrays, slices, maps, structs) e outros tipos personalizados, que só iremos aprender eles nas próximas lições 😊

Até a próxima!

Criadores de Conteúdo

Foto do William Lima
William Lima
Fundador da Micilini

Inventor nato, escreve conteudos de programação para o portal da micilini.

Torne-se um MIC 🤖

Mais de 100 mic's já estão conectados na plataforma.