← Voltar para o blog

Go 1.9: Novidades e Recursos

Descubra as principais novidades do Go 1.9, incluindo novos recursos, melhorias de performance e mudancas na biblioteca padrao.

Novidades do Go 1.9: Uma Análise Detalhada

O Go 1.9, lançado em 24 de agosto de 2017, introduziu diversas melhorias significativas na linguagem, oferecendo recursos novos e otimizações que impactaram tanto o desenvolvimento quanto o desempenho de aplicações Go. Esta versão focou em aprimorar a concorrência, a legibilidade do código e a modularidade. Vamos explorar as principais novidades e mudanças que foram introduzidas.

Type Aliases: Simplificando a Refatoração e a Evolução de APIs

Uma das adições mais importantes no Go 1.9 é o conceito de type aliases. Um type alias permite que um novo nome seja dado a um tipo existente, sem criar um novo tipo distinto. Isso é particularmente útil para refatoração de código e evolução de APIs, permitindo que tipos sejam renomeados ou movidos sem quebrar a compatibilidade com código existente.

Sintaxe e Uso Básico

A sintaxe para declarar um type alias é a seguinte:

type NovoNome = TipoExistente

Por exemplo, suponha que você tenha o seguinte código:

package main

import "fmt"

type ID int

func main() {
	var meuID ID = 123
	fmt.Println(meuID)
}

Se você quiser renomear ID para UserID sem quebrar o código existente, você pode usar um type alias:

package main

import "fmt"

type UserID = int // Alias para int
type ID = UserID   // Alias para UserID, que é int

func main() {
	var meuID ID = 123
	fmt.Println(meuID)

	var outroID UserID = 456
	fmt.Println(outroID)
}

Neste exemplo, UserID e ID são aliases para int. Isso significa que eles são intercambiáveis, e o código existente que usa ID continuará funcionando sem modificações.

Casos de Uso Comuns

Os type aliases são particularmente úteis em cenários como:

  • Refatoração de Código: Renomear tipos complexos sem interromper a compatibilidade.
  • Evolução de APIs: Introduzir novos nomes para tipos existentes, facilitando a transição para novas versões de bibliotecas.
  • Migração de Pacotes: Mover tipos entre pacotes sem exigir que os usuários atualizem seu código imediatamente.

Considere um exemplo de uma biblioteca que define um tipo chamado LegacyType:

package legacy

type LegacyType struct {
	Value string
}

func (l LegacyType) String() string {
	return "Legacy: " + l.Value
}

Em uma nova versão da biblioteca, você quer introduzir um novo tipo chamado NewType, mas ainda precisa manter a compatibilidade com o código existente que usa LegacyType. Você pode usar um type alias para alcançar isso:

package modern

import "legacy"

type NewType = legacy.LegacyType // NewType é um alias para LegacyType

func (n NewType) String() string {
	return "Modern: " + n.Value
}

Agora, o código que usa legacy.LegacyType pode ser atualizado gradualmente para usar modern.NewType sem causar problemas de compatibilidade.

Considerações Importantes

É crucial entender que um type alias não cria um novo tipo. Ele simplesmente fornece um novo nome para um tipo existente. Isso significa que os dois nomes são completamente intercambiáveis. Se você precisa de um novo tipo distinto, você deve usar uma declaração de tipo normal (sem o =).

Concorrência Aprimorada com Subtests e Sub-benchmarks

O Go 1.9 introduziu subtests e sub-benchmarks, que permitem a criação de testes e benchmarks mais estruturados e organizados. Isso facilita a identificação de gargalos de desempenho e problemas em partes específicas do seu código.

Subtests

Subtests permitem que você execute múltiplos testes dentro de uma única função de teste, usando o método t.Run. Cada subtest tem seu próprio nome e pode ser executado individualmente ou em conjunto com outros subtests.

Exemplo:

package main

import (
	"testing"
)

func TestCalculadora(t *testing.T) {
	t.Run("Adicao", func(t *testing.T) {
		resultado := 2 + 3
		if resultado != 5 {
			t.Errorf("Adição falhou: esperado %d, obtido %d", 5, resultado)
		}
	})

	t.Run("Subtracao", func(t *testing.T) {
		resultado := 5 - 2
		if resultado != 3 {
			t.Errorf("Subtração falhou: esperado %d, obtido %d", 3, resultado)
		}
	})
}

Para executar um subtest específico, você pode usar a flag -run com o nome do subtest:

go test -run TestCalculadora/Adicao

Isso executará apenas o subtest “Adicao” dentro da função TestCalculadora.

Sub-benchmarks

Da mesma forma que subtests, sub-benchmarks permitem que você execute múltiplos benchmarks dentro de uma única função de benchmark, usando o método b.Run.

Exemplo:

package main

import (
	"strings"
	"testing"
)

func BenchmarkStringConcat(b *testing.B) {
	b.Run("Plus", func(b *testing.B) {
		for i := 0; i < b.N; i++ {
			_ = "hello" + "world"
		}
	})

	b.Run("Builder", func(b *testing.B) {
		for i := 0; i < b.N; i++ {
			var builder strings.Builder
			builder.WriteString("hello")
			builder.WriteString("world")
			_ = builder.String()
		}
	})
}

Para executar um sub-benchmark específico, você pode usar a flag -run com o nome do sub-benchmark:

go test -bench BenchmarkStringConcat/Builder

Isso executará apenas o sub-benchmark “Builder” dentro da função BenchmarkStringConcat.

Vantagens dos Subtests e Sub-benchmarks

  • Organização: Permitem estruturar testes e benchmarks de forma mais lógica e hierárquica.
  • Facilidade de Depuração: Facilitam a identificação de falhas e gargalos de desempenho em partes específicas do código.
  • Execução Seletiva: Permitem executar apenas os testes ou benchmarks relevantes para uma determinada mudança.

Build Cache: Acelerando o Tempo de Compilação

O Go 1.9 introduziu um build cache persistente, que armazena os resultados intermediários da compilação. Isso pode acelerar significativamente o tempo de compilação, especialmente em projetos grandes com muitas dependências.

Como Funciona

Quando você compila um pacote Go, o compilador armazena os objetos compilados no build cache. Na próxima vez que você compilar o mesmo pacote (ou um pacote que dependa dele), o compilador pode reutilizar os objetos em cache, em vez de recompilá-los do zero.

O build cache é habilitado por padrão e armazena os dados no diretório $HOME/.cache/go-build (ou $XDG_CACHE_HOME/go-build se a variável de ambiente XDG_CACHE_HOME estiver definida).

Limpando o Cache

Você pode limpar o build cache usando o comando go clean -cache:

go clean -cache

Isso removerá todos os objetos compilados do cache, forçando o compilador a recompilar tudo na próxima vez que você executar o comando go build ou go test.

Desabilitando o Cache

Em casos raros, você pode querer desabilitar o build cache. Você pode fazer isso definindo a variável de ambiente GOCACHE para off:

export GOCACHE=off

É importante notar que desabilitar o build cache pode aumentar significativamente o tempo de compilação.

Impacto no Desempenho

O build cache pode ter um impacto significativo no tempo de compilação, especialmente em projetos grandes. Em alguns casos, o tempo de compilação pode ser reduzido em até 90%.

Melhorias na Biblioteca Padrão

O Go 1.9 também trouxe algumas melhorias e adições à biblioteca padrão. Algumas das mudanças notáveis incluem:

Pacote sync/atomic: Atomic Load and Store para Tipos Float

O pacote sync/atomic foi estendido para incluir funções atômicas de carga e armazenamento para tipos de ponto flutuante (float32 e float64). Isso permite que você manipule valores de ponto flutuante de forma segura em ambientes concorrentes.

Exemplo:

package main

import (
	"fmt"
	"sync/atomic"
)

func main() {
	var valor atomic.Float64
	valor.Store(3.14)

	novoValor := valor.Load()
	fmt.Println(novoValor)
}

Pacote math/bits: Funções de Manipulação de Bits

O pacote math/bits foi adicionado, fornecendo funções para manipulação de bits, como contagem de bits definidos (popcount), rotação de bits e inversão de bits. Essas funções são úteis em diversas aplicações, como criptografia, compressão de dados e algoritmos de baixo nível.

Exemplo:

package main

import (
	"fmt"
	"math/bits"
)

func main() {
	x := uint(10) // 1010 em binário
	popCount := bits.OnesCount(x)
	fmt.Println("Número de bits 1:", popCount) // Saída: 2
}

Pacote net/http: Suporte a HTTP/2 Push

O pacote net/http foi aprimorado para oferecer suporte a HTTP/2 Push, permitindo que o servidor envie recursos para o cliente antes que ele os solicite explicitamente. Isso pode melhorar o desempenho de aplicações web, reduzindo o tempo de carregamento das páginas.

Para usar HTTP/2 Push, você pode usar o método Pusher.Push na interface http.ResponseWriter:

package main

import (
	"fmt"
	"net/http"
)

func handler(w http.ResponseWriter, r *http.Request) {
	pusher, ok := w.(http.Pusher)
	if ok {
		// Tenta fazer o push do arquivo CSS
		if err := pusher.Push("/static/style.css", nil); err != nil {
			fmt.Printf("Falha ao fazer push do CSS: %v\n", err)
		}
	}
	fmt.Fprintf(w, "Olá, HTTP/2 Push!")
}

func main() {
	http.HandleFunc("/", handler)
	// Certifique-se de ter um certificado TLS válido para usar HTTP/2
	err := http.ListenAndServeTLS(":443", "cert.pem", "key.pem", nil)
	if err != nil {
		fmt.Println("Erro ao iniciar o servidor:", err)
	}
}

Neste exemplo, o servidor tenta fazer o push do arquivo /static/style.css para o cliente antes que ele o solicite. Isso pode reduzir o tempo de carregamento da página, pois o cliente já terá o arquivo CSS em cache quando precisar dele.

Como Atualizar para o Go 1.9

Para atualizar para o Go 1.9, você pode usar o comando go get:

go get golang.org/dl/go1.9
go1.9 download
go1.9 use

Este comando irá baixar e instalar o Go 1.9 em seu sistema. Depois de instalado, você pode usar o comando go version para verificar se a atualização foi bem-sucedida:

go version

A saída deve mostrar go version go1.9.

Conclusão Prática

O Go 1.9 trouxe melhorias significativas que impactaram positivamente o desenvolvimento e o desempenho de aplicações Go. Os type aliases facilitaram a refatoração e a evolução de APIs, os subtests e sub-benchmarks aprimoraram a concorrência e a organização de testes, e o build cache acelerou o tempo de compilação. Além disso, as adições à biblioteca padrão, como as funções atômicas para tipos float e as funções de manipulação de bits, expandiram as capacidades da linguagem.

Ao atualizar para o Go 1.9, os desenvolvedores puderam aproveitar esses novos recursos e melhorias para criar aplicações mais eficientes, robustas e fáceis de manter. A adoção dos type aliases, subtests/sub-benchmarks e a conscientização sobre o build cache podem otimizar o fluxo de trabalho e melhorar a qualidade do código.

Para informações mais detalhadas, consulte as notas de lançamento oficiais do Go 1.9: https://go.dev/doc/go1.9