Operadores, expressões e conversão de tipo

Artigo 04 — Operadores, expressões e conversão de tipos

Curso: Dominando Go em 1 Ano Prof. Ricardo Matos Módulo 1 — Fundamentos da Linguagem


Operadores são a gramática das expressões

Se variáveis são os substantivos de um programa, operadores são os verbos. Eles definem o que acontece com os dados — como são combinados, comparados, transformados e manipulados. Go tem um conjunto de operadores enxuto e bem definido, sem surpresas ou comportamentos ambíguos.


Operadores aritméticos

Os operadores aritméticos básicos funcionam como esperado para tipos numéricos:

package main

import "fmt"

func main() {
    a := 15
    b := 4

    fmt.Println(a + b)   // 19  — adição
    fmt.Println(a - b)   // 11  — subtração
    fmt.Println(a * b)   // 60  — multiplicação
    fmt.Println(a / b)   // 3   — divisão inteira
    fmt.Println(a % b)   // 3   — resto da divisão (módulo)
}

Um ponto que merece atenção é a divisão inteira. Quando ambos os operandos são inteiros, o resultado também é inteiro — a parte fracionária é descartada sem arredondamento. Para obter o resultado com casas decimais, ao menos um dos operandos precisa ser float64:

a := 15
b := 4

inteiro := a / b                        // 3
decimal := float64(a) / float64(b)      // 3.75

fmt.Println(inteiro, decimal)

Go não possui operador de exponenciação como **. Para potências, usa-se a função math.Pow:

import "math"

resultado := math.Pow(2, 10)  // 1024.0
fmt.Println(resultado)

Operadores de incremento e decremento

Go possui ++ e --, mas com uma diferença importante em relação a C e Java: eles são declarações, não expressões. Isso significa que não retornam valor e só podem ser usados de forma isolada, nunca dentro de uma expressão maior:

x := 10
x++         // válido: x agora é 11
x--         // válido: x agora é 10

// y := x++ — erro de compilação
// if x++ > 5 — erro de compilação

Além disso, Go não possui as formas prefixadas ++x e --x. Apenas o sufixo é permitido.


Operadores de atribuição composta

Go oferece versões combinadas dos operadores aritméticos e bitwise com atribuição:

x := 10

x += 5   // x = x + 5  → 15
x -= 3   // x = x - 3  → 12
x *= 2   // x = x * 2  → 24
x /= 4   // x = x / 4  → 6
x %= 4   // x = x % 4  → 2

fmt.Println(x) // 2

Operadores de comparação

Operadores de comparação sempre retornam um valor bool. São usados principalmente em estruturas de controle:

a := 10
b := 20

fmt.Println(a == b)  // false — igualdade
fmt.Println(a != b)  // true  — diferença
fmt.Println(a < b)   // true  — menor que
fmt.Println(a > b)   // false — maior que
fmt.Println(a <= b)  // true  — menor ou igual
fmt.Println(a >= b)  // false — maior ou igual

Em Go, comparações entre tipos diferentes são proibidas pelo compilador. Não é possível comparar um int com um float64 diretamente — a conversão precisa ser explícita antes.


Operadores lógicos

Usados para combinar expressões booleanas:

t := true
f := false

fmt.Println(t && f)  // false — E lógico (ambos precisam ser true)
fmt.Println(t || f)  // true  — OU lógico (pelo menos um precisa ser true)
fmt.Println(!t)      // false — negação

Go utiliza avaliação em curto-circuito. No operador &&, se o primeiro operando for false, o segundo não é avaliado. No operador ||, se o primeiro for true, o segundo é ignorado. Esse comportamento é importante quando o segundo operando é uma chamada de função com efeitos colaterais ou que pode causar pânico.

func perigosa() bool {
    fmt.Println("função executada")
    return true
}

resultado := false && perigosa()
// "função executada" NÃO é impresso — curto-circuito
fmt.Println(resultado) // false

Operadores bitwise

Para manipulação em nível de bits, Go oferece o conjunto completo de operadores bitwise:

a := 0b1100  // 12 em binário
b := 0b1010  // 10 em binário

fmt.Println(a & b)   // 0b1000 = 8  — AND bitwise
fmt.Println(a | b)   // 0b1110 = 14 — OR bitwise
fmt.Println(a ^ b)   // 0b0110 = 6  — XOR bitwise
fmt.Println(a &^ b)  // 0b0100 = 4  — AND NOT (bit clear)
fmt.Println(a << 1)  // 0b11000 = 24 — deslocamento à esquerda
fmt.Println(a >> 1)  // 0b0110 = 6  — deslocamento à direita

O operador &^ é exclusivo do Go e não tem equivalente direto em C ou Java. Ele limpa os bits do operando esquerdo que estão definidos no operando direito.


Precedência de operadores

Quando múltiplos operadores aparecem em uma expressão, Go segue uma ordem de precedência definida. Do maior para o menor nível:

Nível Operadores
5 (maior) * / % << >> & &^
4 + - | ^
3 == != < <= > >=
2 &&
1 (menor) ||

A recomendação prática é usar parênteses sempre que a precedência não for óbvia. Código claro é preferível a código que exige que o leitor memorize tabelas:

// Ambíguo para o leitor:
resultado := 2 + 3*4 - 1

// Explícito e sem dúvida:
resultado := 2 + (3 * 4) - 1

Conversão de tipos em profundidade

Como visto no artigo anterior, Go não realiza conversões implícitas. Toda conversão entre tipos diferentes deve ser escrita explicitamente pelo programador. A sintaxe é sempre Tipo(valor).

Conversões numéricas:

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

fmt.Println(i, f, u) // 42 42 42

Atenção com truncamento. Converter float64 para int descarta a parte decimal sem arredondamento:

x := 3.99
y := int(x)
fmt.Println(y) // 3 — não 4

Conversão entre inteiros de tamanhos diferentes. Converter um valor grande para um tipo menor pode causar overflow silencioso:

var grande int32 = 300
var pequeno int8 = int8(grande)
fmt.Println(pequeno) // 44 — overflow! 300 não cabe em int8

Go não emite erro em tempo de execução para overflow em conversões — o valor simplesmente é truncado. Essa é uma armadilha que exige atenção.

Conversão entre string e slice de bytes:

s := "Olá, Go"
b := []byte(s)         // string → []byte
s2 := string(b)        // []byte → string

fmt.Println(b)         // [79 108 195 161 44 32 71 111]
fmt.Println(s2)        // Olá, Go

Essa conversão é muito comum ao trabalhar com I/O, redes e manipulação de texto.

Conversão entre string e slice de runes:

s := "Olá"
r := []rune(s)
fmt.Println(len(s))    // 4 bytes — o 'á' ocupa 2 bytes em UTF-8
fmt.Println(len(r))    // 3 runes — 3 caracteres visuais

Quando a contagem de caracteres visuais importa, sempre converta para []rune antes de usar len.


O pacote strconv: conversões entre string e tipos numéricos

Converter números em strings e vice-versa é uma operação frequente. O pacote strconv da biblioteca padrão é a ferramenta correta para isso — nunca use conversão direta string(numero) com inteiros, pois o resultado é inesperado:

fmt.Println(string(65)) // "A" — não "65"!

O que acontece acima é que Go interpreta 65 como um ponto de código Unicode e retorna o caractere correspondente. Para converter o número 65 na string "65", use strconv:

import (
    "fmt"
    "strconv"
)

func main() {
    // int → string
    n := 42
    s := strconv.Itoa(n)
    fmt.Println(s)          // "42"
    fmt.Printf("%T\n", s)  // string

    // string → int
    valor, err := strconv.Atoi("123")
    if err != nil {
        fmt.Println("Erro na conversão:", err)
        return
    }
    fmt.Println(valor + 1)  // 124

    // float64 → string
    f := 3.14159
    fs := strconv.FormatFloat(f, 'f', 2, 64)
    fmt.Println(fs)  // "3.14"

    // string → float64
    fv, err := strconv.ParseFloat("2.718", 64)
    if err == nil {
        fmt.Println(fv)  // 2.718
    }
}

A função strconv.Atoi retorna dois valores: o inteiro convertido e um erro. Esse padrão de retorno duplo é o idioma central do Go para tratamento de falhas e será explorado em profundidade nos próximos módulos.


Expressões e o pacote fmt

O pacote fmt oferece funções para formatar e imprimir expressões com precisão:

nome := "Ricardo"
idade := 35
altura := 1.78

fmt.Printf("Nome: %s\n", nome)
fmt.Printf("Idade: %d\n", idade)
fmt.Printf("Altura: %.2f\n", altura)
fmt.Printf("Tipo de idade: %T\n", idade)

// Formatação para string sem imprimir
s := fmt.Sprintf("Usuário: %s, %d anos", nome, idade)
fmt.Println(s)

Os verbos de formatação mais usados são %d para inteiros, %f para floats, %s para strings, %t para booleanos, %T para o tipo da variável e %v para o valor padrão de qualquer tipo.


Resumo do que foi coberto

Este artigo percorreu o conjunto completo de operadores do Go — aritméticos, lógicos, de comparação, bitwise e de atribuição composta — junto com as regras de precedência. A conversão explícita de tipos foi aprofundada com atenção aos casos armadilhosos como truncamento e overflow. O pacote strconv foi apresentado como a ferramenta correta para conversões entre strings e tipos numéricos.


Referências e leituras complementares


Próximo artigo: Artigo 05 — Estruturas de controle: if, for e switch


you asked

Sim


claude response