O Go 1.26 introduziu uma implementação totalmente nova do subcomando go fix, projetado para auxiliar na modernização e manutenção do código Go. Uma das funcionalidades mais notáveis é o “inliner” no nível do código-fonte (source-level inliner), que permite que autores de pacotes expressem migrações e atualizações de API de forma direta e segura. Este artigo explora o que é o inliner, como usá-lo e alguns dos desafios e tecnologias por trás dele.
O que é Source-Level Inlining?
O “source-level inlining” é uma técnica que substitui uma chamada de função por uma cópia do corpo da função chamada, substituindo os argumentos pelos parâmetros correspondentes. Diferente dos inliners tradicionais encontrados em compiladores, que operam em representações intermediárias, o inliner do Go modifica o código-fonte diretamente.
Se você já utilizou a refatoração interativa “Inline call” do gopls, você já utilizou o inliner no nível do código-fonte. Essa ferramenta é crucial para várias transformações de código, como “Change signature” e “Remove unused parameter”, pois lida com sutilezas que podem surgir durante a refatoração de chamadas de função.
O mesmo inliner agora faz parte do comando go fix, habilitando migrações e atualizações de API “self-service” através de um novo comentário diretivo: //go:fix inline.
Exemplos Práticos de Uso
Renomeando ioutil.ReadFile
No Go 1.16, a função ioutil.ReadFile foi depreciada em favor de os.ReadFile. Para facilitar a transição, o inliner pode ser usado. Primeiro, anota-se a função antiga com //go:fix inline:
package ioutil
import "os"
// ReadFile reads the file named by filename…
// Deprecated: As of Go 1.16, this function simply calls [os.ReadFile].
//go:fix inline
func ReadFile(filename string) ([]byte, error) {
return os.ReadFile(filename)
}
Ao executar go fix em um arquivo que chama ioutil.ReadFile, a chamada é substituída por os.ReadFile:
$ go fix -diff ./...
-import "io/ioutil"
+import "os"
- data, err := ioutil.ReadFile("hello.txt")
+ data, err := os.ReadFile("hello.txt")
A principal vantagem dessa abordagem é que ela é intrinsecamente mais segura do que ferramentas de reescrita arbitrária como gofmt -r, pois substitui a chamada por uma cópia do corpo da função, preservando o comportamento do programa (exceto em casos raros que dependem da inspeção da pilha de chamadas).
Corrigindo Falhas de Design de API
O inliner também pode ser usado para corrigir falhas de design de API. Considere um pacote hipotético oldmath com os seguintes problemas:
- A função
Subtem a ordem dos parâmetros invertida. - A função
Infimplicitamente prefere um dos infinitos. - A função
Negé redundante.
// Package oldmath is the bad old math package.
package oldmath
// Sub returns x - y.
func Sub(y, x int) int
// Inf returns positive infinity.
func Inf() float64
// Neg returns -x.
func Neg(x int) int
Para migrar os usuários para um pacote newmath que corrige esses problemas, o primeiro passo é implementar a API antiga em termos da nova e depreciar as funções antigas. Em seguida, adicione as diretivas de inliner:
// Package oldmath is the bad old math package.
package oldmath
import "newmath"
// Sub returns x - y.
// Deprecated: the parameter order is confusing.
//go:fix inline
func Sub(y, x int) int {
return newmath.Sub(x, y)
}
// Inf returns positive infinity.
// Deprecated: there are two infinite values; be explicit.
//go:fix inline
func Inf() float64 {
return newmath.Inf(+1)
}
// Neg returns -x.
// Deprecated: this function is unnecessary.
//go:fix inline
func Neg(x int) int {
return newmath.Sub(0, x)
}
Agora, ao executar go fix, as chamadas às funções antigas serão substituídas pelas novas:
import "oldmath"
var nine = oldmath.Sub(1, 10) // diagnostic: "call to oldmath.Sub should be inlined"
será transformado em:
import "newmath"
var nine = newmath.Sub(10, 1)
O inliner também funciona com tipos e constantes. Se o pacote oldmath originalmente declarasse um tipo de dados para números racionais e uma constante para π, poderíamos usar as seguintes declarações de encaminhamento para migrá-los para o pacote newmath, preservando o comportamento do código existente:
package oldmath
//go:fix inline
type Rational = newmath.Rational
//go:fix inline
const Pi = newmath.Pi
Implicações e Benefícios
O “source-level inlining” oferece vários benefícios:
- Migrações de API Seguras: Garante que as mudanças de API sejam feitas de forma controlada e previsível, minimizando o risco de introduzir bugs.
- Melhoria da Qualidade do Código: Permite a correção de falhas de design de API e a remoção de código obsoleto.
- Automatização da Manutenção: Facilita a automatização de tarefas de manutenção de código em larga escala.
- Integração com Ferramentas Existentes: O inliner está integrado com ferramentas como
gopls, fornecendo feedback imediato aos desenvolvedores.
Em resumo, o “source-level inlining” no Go 1.26 é uma ferramenta poderosa para modernizar e manter o código Go. Ao permitir que os autores de pacotes expressem migrações de API de forma segura e automatizada, ele simplifica a vida dos desenvolvedores e melhora a qualidade geral do ecossistema Go. O go fix com o inliner representa um passo significativo em direção a um ecossistema Go mais robusto e sustentável.
Artigo Original
Este e um resumo em português do artigo original publicado no blog oficial do Go.
Titulo original: //go:fix inline and the source-level inliner
Leia o artigo completo em ingles no Go Blog
Autor original: Alan Donovan