Operadores e expressões

1. Operadores Aritméticos

Go oferece os operadores aritméticos básicos: soma (+), subtração (-), multiplicação (*), divisão (/) e módulo (%). Um ponto crucial é o comportamento da divisão entre inteiros:

package main

import "fmt"

func main() {
    // Divisão entre inteiros TRUNCA o resultado
    a := 10
    b := 3
    fmt.Println(a / b) // 3 (não 3.33)

    // Para resultado float, pelo menos um operando deve ser float
    x := 10.0
    y := 3.0
    fmt.Println(x / y) // 3.3333333333333335

    // Módulo funciona apenas com inteiros
    fmt.Println(10 % 3) // 1

    // Operadores de atribuição combinada
    contador := 10
    contador += 5  // contador = contador + 5
    contador -= 3  // contador = contador - 3
    contador *= 2  // contador = contador * 2
    contador /= 4  // contador = contador / 4
    contador %= 3  // contador = contador % 3
    fmt.Println(contador) // 0
}

2. Operadores de Comparação

Go implementa os operadores relacionais padrão: ==, !=, >, <, >=, <=. Uma característica importante é que não é possível comparar diretamente tipos diferentes:

package main

import "fmt"

func main() {
    a := 10
    b := 10
    c := 20

    fmt.Println(a == b) // true
    fmt.Println(a != c) // true
    fmt.Println(a > c)  // false
    fmt.Println(a <= c) // true

    // Isto NÃO compila:
    // var x int = 10
    // var y float64 = 10.0
    // fmt.Println(x == y) // erro: tipos incompatíveis

    // Solução: conversão explícita
    fmt.Println(float64(a) == 10.0) // true
}

3. Operadores Lógicos

Os operadores lógicos && (AND), || (OR) e ! (NOT) seguem o padrão de short-circuit evaluation:

package main

import "fmt"

func verificar(valor int) bool {
    fmt.Println("verificando", valor)
    return valor > 5
}

func main() {
    a, b := 3, 10

    // Short-circuit: se a primeira condição é falsa, não avalia a segunda
    if a > 5 && verificar(b) {
        fmt.Println("ambas verdadeiras")
    }
    // "verificando" NÃO será impresso para b

    // OR com short-circuit: se primeira é verdadeira, não avalia segunda
    if a < 5 || verificar(b) {
        fmt.Println("pelo menos uma verdadeira")
    }
    // "verificando" será impresso para b

    // NOT inverte o valor booleano
    ativo := false
    if !ativo {
        fmt.Println("sistema inativo")
    }
}

4. Operadores de Incremento e Decremento

Em Go, ++ e -- são statements, não expressões. Isso significa que não podem ser usados em atribuições ou comparações:

package main

import "fmt"

func main() {
    contador := 0

    // Válido: incremento como statement
    contador++
    fmt.Println(contador) // 1

    contador--
    fmt.Println(contador) // 0

    // Isto NÃO compila em Go:
    // resultado := contador++ // erro: não é uma expressão
    // if contador++ > 5 { }   // erro: não é permitido

    // Uso correto em loops
    for i := 0; i < 3; i++ {
        fmt.Println(i) // 0, 1, 2
    }

    // Alternativa para pós-incremento
    valor := 10
    valor++          // primeiro usa, depois incrementa
    fmt.Println(valor) // 11
}

5. Operadores Bit a Bit (Bitwise)

Go oferece operadores bitwise completos: & (AND), | (OR), ^ (XOR), &^ (AND NOT), << (left shift) e >> (right shift):

package main

import "fmt"

func main() {
    a := 0b1100 // 12 em binário
    b := 0b1010 // 10 em binário

    fmt.Printf("a & b = %04b (%d)\n", a&b, a&b)  // 1000 (8)
    fmt.Printf("a | b = %04b (%d)\n", a|b, a|b)  // 1110 (14)
    fmt.Printf("a ^ b = %04b (%d)\n", a^b, a^b)  // 0110 (6)
    fmt.Printf("a &^ b = %04b (%d)\n", a&^b, a&^b) // 0100 (4)

    // Deslocamento de bits
    x := 1         // 0001
    fmt.Println(x << 3) // 8 (1000)
    fmt.Println(8 >> 2) // 2 (0010)

    // Aplicação prática: flags
    const (
        READ  = 1 << iota // 1
        WRITE             // 2
        EXECUTE           // 4
    )

    permissao := READ | WRITE // 3 (011)
    if permissao&READ != 0 {
        fmt.Println("tem permissão de leitura")
    }
}

6. Operadores de Atribuição e Endereço

Go suporta atribuição múltipla e operadores de ponteiro:

package main

import "fmt"

func main() {
    // Atribuição múltipla e troca de valores
    a, b := 10, 20
    a, b = b, a // troca os valores
    fmt.Println(a, b) // 20 10

    // Operador de endereço (&) e dereferência (*)
    x := 42
    p := &x // p é um ponteiro para x

    fmt.Println(p)  // endereço de memória (ex: 0xc0000b2008)
    fmt.Println(*p) // 42 (valor no endereço apontado)

    *p = 100       // altera o valor de x através do ponteiro
    fmt.Println(x) // 100

    // Passagem por referência (simulada com ponteiros)
    incrementar := func(n *int) {
        *n++
    }
    incrementar(&x)
    fmt.Println(x) // 101
}

7. Precedência e Associatividade

Go possui uma hierarquia clara de precedência de operadores. Quando em dúvida, use parênteses:

package main

import "fmt"

func main() {
    // Precedência (da maior para a menor):
    // 1. Unários: !, -, +, &, *, ^, ++, --
    // 2. Multiplicativos: *, /, %, <<, >>, &, &^
    // 3. Aditivos: +, -, |, ^
    // 4. Comparação: ==, !=, <, <=, >, >=
    // 5. Lógicos AND: &&
    // 6. Lógicos OR: ||

    // Exemplo de expressão complexa
    resultado := 2 + 3*4 == 14 && !false
    // Passo a passo:
    // 1. 3*4 = 12
    // 2. 2+12 = 14
    // 3. 14 == 14 = true
    // 4. !false = true
    // 5. true && true = true
    fmt.Println(resultado) // true

    // Use parênteses para clareza
    resultado2 := ((2 + (3 * 4)) == 14) && (!false)
    fmt.Println(resultado2) // true

    // Exemplo onde precedência faz diferença
    fmt.Println(3 + 4 * 2)      // 11 (multiplicação primeiro)
    fmt.Println((3 + 4) * 2)    // 14 (parênteses forçam soma)
}

8. Expressões com Tipos Específicos

Go exige conversão explícita entre tipos, e operações com strings têm comportamentos específicos:

package main

import (
    "fmt"
    "math"
)

func main() {
    // Conversão explícita de tipos
    var f float64 = 3.14
    var i int = int(f) // 3 (truncado)
    fmt.Println(i)

    // Conversão segura com verificação
    if f <= math.MaxInt32 {
        i := int32(f)
        fmt.Println(i)
    }

    // Concatenação de strings
    nome := "João"
    saudacao := "Olá, " + nome + "!"
    fmt.Println(saudacao) // Olá, João!

    // Comparação de strings (lexicográfica)
    fmt.Println("abc" < "abd") // true
    fmt.Println("Go" == "go")  // false (case-sensitive)

    // Cuidado com overflow em inteiros
    var max uint8 = 255
    max++ // wraparound: volta para 0
    fmt.Println(max) // 0

    // Underflow
    var min uint8 = 0
    min-- // wraparound: vai para 255
    fmt.Println(min) // 255

    // Para evitar overflow, use tipos maiores ou verifique limites
    var seguro uint64 = math.MaxUint64
    if seguro < math.MaxUint64 {
        seguro++
    }
}

Referências