1 Introdução aos Tipos de Dados da Linguagem Go

Na linguagem Go, os tipos de dados são a base da programação, determinando a forma dos dados que as variáveis podem armazenar. Os tipos de dados básicos fornecidos pela linguagem Go são principalmente divididos nas seguintes categorias:

Tipo de Dado Descrição Uso de Memória
bool Tipo booleano, usado para armazenar verdadeiro ou falso 1 byte
int, uint Inteiros com e sem sinal, o tamanho padrão depende da plataforma do sistema 4 ou 8 bytes
int8, uint8 Inteiros com e sem sinal de 8 bits 1 byte
int16, uint16 Inteiros com e sem sinal de 16 bits 2 bytes
int32, uint32 Inteiros com e sem sinal de 32 bits 4 bytes
int64, uint64 Inteiros com e sem sinal de 64 bits 8 bytes
float32 Número de ponto flutuante de 32 bits 4 bytes
float64 Número de ponto flutuante de 64 bits 8 bytes
complex64 Número complexo com partes real e imaginária de 32 bits 8 bytes
complex128 Número complexo com partes real e imaginária de 64 bits 16 bytes
byte Semelhante a uint8 1 byte
rune Semelhante a int32, representando um ponto de código Unicode 4 bytes
string Tipo string Depende do comprimento da string
error Interface de erro, usada para retornar informações de erro Sem tamanho fixo

Esses tipos podem ser escolhidos de acordo com diferentes necessidades, como cálculos numéricos, processamento de texto ou controle lógico.

2 Tipos de Dados Inteiros

2.1 Visão Geral dos Tipos de Inteiros

A linguagem Go possui múltiplos tipos de inteiros integrados, classificados da seguinte forma:

  • Inteiros com sinal: int8, int16, int32 (ou rune), int64 e int
  • Inteiros sem sinal: uint8 (ou byte), uint16, uint32, uint64 e uint

Os tamanhos de int e uint são de 4 bytes em sistemas de 32 bits e 8 bytes em sistemas de 64 bits. Os intervalos de valores dos tipos de dados inteiros são mostrados na tabela abaixo:

Tipo Intervalo de Valores
int8 -128 a 127
uint8 0 a 255
int16 -32768 a 32767
uint16 0 a 65535
int32 -2147483648 a 2147483647
uint32 0 a 4294967295
int64 -9223372036854775808 a 9223372036854775807
uint64 0 a 18446744073709551615

2.2 Uso de Variáveis Inteiras

A sintaxe básica para declarar uma variável inteira é a seguinte:

var nome_variável tipo_dados = valor_inicial

Exemplo de código:

package main
import "fmt"

func main() {
    var a int = 10 // Variável de inteiro com sinal
    var b uint = 20 // Variável de inteiro sem sinal
    var c int8 = -128 // Menor valor de int8
    fmt.Println(a, b, c)
}

2.3 Operações com Inteiros

A linguagem Go suporta operadores aritméticos comuns, como adição (+), subtração (-), multiplicação (*), divisão (/) e módulo (%), bem como operadores bitwise como E bitwise (&), OU (|), XOR (^), deslocamento para a esquerda (<<) e deslocamento para a direita (>>).

package main
import "fmt"

func main() {
    x := 10
    y := 3

    // Operações aritméticas
    fmt.Println(x + y) // Adição
    fmt.Println(x - y) // Subtração
    fmt.Println(x * y) // Multiplicação
    fmt.Println(x / y) // Divisão
    fmt.Println(x % y) // Módulo

    // Operações bitwise
    fmt.Println(x & y)  // E bitwise
    fmt.Println(x | y)  // OU bitwise
    fmt.Println(x ^ y)  // XOR bitwise
    fmt.Println(x << 1) // Deslocamento para a esquerda por 1
    fmt.Println(x >> 1) // Deslocamento para a direita por 1
}

3 Tipos de Dados de Ponto Flutuante

3.1 Visão Geral dos Tipos de Dados de Ponto Flutuante

Na linguagem Go, os tipos de ponto flutuante incluem float32 e float64, correspondendo a dados de ponto flutuante de 32 bits e 64 bits. Em geral, é recomendado usar float64 porque fornece um intervalo maior e uma precisão mais acurada.

  • float32 tem aproximadamente 23 bits significativos, fornecendo cerca de 7 dígitos decimais de precisão.
  • float64 tem aproximadamente 52 bits significativos, fornecendo cerca de 16 dígitos decimais de precisão.

3.2 Utilizando Variáveis de Ponto Flutuante

Variáveis de ponto flutuante podem ser declaradas fornecendo literais diretamente ou usando a palavra-chave var:

package main
import "fmt"

func main() {
    var f1 float32 = 3.14 // Especificação explícita do tipo float32
    f2 := 3.14            // Inferido automaticamente como tipo float64
    fmt.Println(f1, f2)
}

3.3 Aritmética de Ponto Flutuante e Problemas

A aritmética de ponto flutuante pode resultar em perda de precisão. Realizar operações com alta precisão é um problema comum, especialmente ao subtrair dois números muito próximos.

package main
import "fmt"

func main() {
    f1 := .1
    f2 := .2
    f3 := f1 + f2
    fmt.Println(f3) // A saída pode não ser esperada como .3 devido a problemas de precisão

    // Corrigindo problemas de precisão usando saída formatada
    fmt.Printf("%.1f\n", f3) // Saída corrigida para .3
}

4 Tipo de Dado Booleano

4.1 Visão Geral do Tipo de Dado Booleano

Booleano é o tipo de dado mais simples e pode ter apenas dois valores: true (verdadeiro) e false (falso). Ele possui uma posição muito importante em declarações condicionais e estruturas de controle de loop.

4.2 Utilizando Variáveis Booleanas

Declaração e utilização de variáveis booleanas:

package main
import "fmt"

func main() {
    var sucesso bool = true
    var falha bool = false
    fmt.Println("Operação bem-sucedida:", sucesso)
    fmt.Println("Operação falhou:", falha)
}

Valores booleanos são frequentemente utilizados em declarações condicionais:

package main
import "fmt"

func main() {
    a := 10
    b := 20
    fmt.Println("a == b:", a == b) // false
    fmt.Println("a < b:", a < b)   // true
}

5 Tipo de Dado String

5.1 Visão Geral de String

Uma string é uma coleção de caracteres. Na linguagem Go, as strings são imutáveis. Cada string consiste em duas partes: um ponteiro para o array de bytes subjacente e um comprimento. Strings podem conter qualquer dado, incluindo bytes.

5.2 Utilizando Variáveis de String

Variáveis de string são tipicamente declaradas usando aspas duplas " para criação, mas também é possível usar acento grave ` para criar strings de várias linhas:

package main
import "fmt"

func main() {
    var s1 string = "olá"
    s2 := "mundo"
    s3 := `Isto é uma 
    string de 
    múltiplas linhas`
    fmt.Println(s1)
    fmt.Println(s2)
    fmt.Println(s3)
}

Uma vez que uma string é criada, seu conteúdo não pode ser alterado. A seguinte operação é ilegal e resultará em um erro de compilação:

s := "olá"
s[] = 'O` // Erro de compilação: conteúdo da string é imutável

5.3 Operações com String

Strings são muito comuns e importantes na programação. A linguagem Go fornece um conjunto rico de funções integradas para manipulação de strings. Aqui estão algumas operações comumente utilizadas.

5.3.1 Concatenação de String

Na linguagem Go, você pode usar o operador de adição (+) para concatenar strings, que é o método mais direto. Além disso, ao lidar com a concatenação frequente de múltiplas strings, é recomendado usar strings.Builder para melhor desempenho.

package main

import (
    "fmt"
    "strings"
)

func main() {
    // Concatenando strings usando +
    oi := "Olá, "
    mundo := "Mundo!"
    resultado := oi + mundo
    fmt.Println(resultado) // Saída: Olá, Mundo!

    // Concatenando strings usando strings.Builder
    var sb strings.Builder
    sb.WriteString("Olá, ")
    sb.WriteString("Mundo!")
    fmt.Println(sb.String()) // Saída: Olá, Mundo!
}

5.3.2 Divisão de Strings

A divisão de strings pode ser feita usando a função strings.Split, que divide a string em uma lista com base no delimitador especificado.

pacote principal

import (
    "fmt"
    "strings"
)

func main() {
    // Define uma string
    sentença := "Go é uma linguagem de programação de código aberto"

    // Divida a string por espaço
    palavras := strings.Split(sentença, " ")
    for _, palavra := range palavras {
        fmt.Printf("%s\n", palavra)
    }
    // Resultado:
    // Go
    // é
    // uma
    // linguagem
    // de
    // programação
    // de
    // código
    // aberto
}

5.3.3 Acesso por Índice

Em Go, uma string é uma sequência imutável de bytes. Você pode usar indexação para acessar bytes específicos em uma string. No entanto, é importante notar que, como uma string pode conter caracteres de vários bytes (como caracteres codificados em UTF-8), a indexação direta pode não resultar no caractere único esperado.

pacote principal

import "fmt"

func main() {
    s := "Olá, 世界"
    for i := 0; i < len(s); i++ {
        fmt.Printf("%d: %x\n", i, s[i])
    }
    // Observação: isso irá produzir a representação hexadecimal dos bytes, não dos caracteres
}

Para iterar pela string por caractere, você pode usar o loop range.

pacote principal

import "fmt"

func main() {
    s := "Olá, 世界"
    for índice, valorRune := range s {
        fmt.Printf("%d: %U '%c'\n", índice, valorRune, valorRune)
    }
    // Resultado: índice, codificação Unicode e o próprio caractere para cada caractere
}

5.3.4 Obtenção do Comprimento

A função len pode obter o comprimento de uma string, ou seja, o comprimento da sequência de bytes subjacente. Para strings UTF-8, se você precisa obter o número de caracteres (runes), deve-se usar a função utf8.RuneCountInString. Isso pode lidar corretamente com caracteres de vários bytes.

pacote principal

import (
    "fmt"
   "unicode/utf8"
)

func main() {
    s := "Olá, 世界"
    fmt.Println("Comprimento de bytes:", len(s)) // Resultado: comprimento em bytes
    fmt.Println("Comprimento de runes:", utf8.RuneCountInString(s)) // Resultado: comprimento dos caracteres
}

A partir do exemplo acima, podemos ver que a linguagem Go fornece funções de biblioteca ricas para operações de strings, facilitando a realização de várias tarefas de processamento de strings. Ao codificar, é importante prestar atenção à distinção entre bytes e caracteres, especialmente ao lidar com conjuntos de caracteres não ASCII.