
“I’ve made a new discovery! The Pikmin can choose their own routes! But…does this indicate rational thought or just basic instinct? Unfortunately, I cannot determine that at this point. I will be vigilant in my studies, though…”
Por volta de 2017/2019 (quase 10 anos atrás 😭), eu dei o meu primeiro pulo no Rust Na época, o ecossistema era bem menor, e para um desenvolvedor iniciante que só sabia Lua e Javascript, coisas como ter que esperar para rodar o programa (compilação) e ter que se preocupar com o compilador reclamando, eram só problemas criados sem motivo algum
O tempo passou, e nesse meio tempo eu aprendi muita coisa, me apaixonei por Go, mexi bastante com C, e trabalhei em um bocadinho de projetos
Esse é o primeiro post do Diário de Bordo Rust: uma série de postagens onde vou tentar explicar coisas do Rust para desenvolvedores Go, à medida que eu mesmo vou aprendendo (ou reaprendendo) a linguagem
“Go oferece simplicidade, Rust oferece expressividade”
Acho que esse é um dos grandes motivos de o pessoal falar que ele tem uma grande curva de aprendizado
Uma coisa que você vai perceber escrevendo Rust é que a linguagem te oferece diferentes formas de fazer a mesma coisa. Existe muita oportunidade para melhorar o código e deixar ele mais claro e direto
Você vai ver seu código envelhecer bem rápidinho conforme você aprende novos truques e vira um programador Rust melhor
“Sem
nil, semnull”
Em Go você provavelmente já viu ou escreveu código parecido com isso:
if value != nil {
// usando o valor de forma "segura"
}
// No Go isso é seguro, o pior que pode acontecer é um panic
// (em C, um ponteiro nulo pode virar undefined behavior/segfault)
E talvez você já até tenha esquecido de checar por nil
alguma vez
Você não deve ver nulos no nosso amigo Rust, por conta do enum Option<T> e
do compilador, que obriga que a gente trate isso
Basicamente, se você chama uma função onde o valor pode ser vazio, você provavelmente vai receber um Option<T>. Ele vem com 2 valorezinhos, Some(T) e None, e Rust obriga que você defina um match para cada valor, para dizer o que deve acontecer se o valor
vier como None (vazio), ou se veio como Some (algo dentro)
Você também pode ser explícito e só dizer .unwrap(), que é basicamente dizer que você garante que vai ter algo, e se não tiver pode dar panic (tipo esquecer de fazer um nil check no Go)
“if err != nil”
Seguindo a mesma regra do nulo, se você recebe um valor que
pode ter um erro, você provavelmente vai receber um valor do tipo enum Result<T, E>
Esse carinha é bem parecido com o Option, ele vem com Ok(T) (retorno sem erro)
ou Err(E) (retorno com erro)
Você pode ignorar o erro usando o .unwrap() (se houver Err, dá panic)
Em Go, quando você não quer tratar o erro ali mesmo, você propaga ele pra cima:
func fazerTalCoisa() error {
resultado, err := coisar()
if err != nil {
return err // retornando o erro
}
// usando o resultado...
return nil
}
Em Rust existe um jeito bem mais curto de fazer a mesma coisa: o operador ?
Se o valor for Err, a função já retorna esse erro na hora, sem precisar
escrever o if:
fn fazer_tal_coisa() -> Result<(), std::io::Error> { // tipo do erro explícito
let resultado = coisar()?; // se coisar() der Err, a função já retorna o erro
// usando o resultado...
Ok(()) // deu tudo certo, retorna Ok sem valor nenhum dentro
}
Repara no tipo de retorno: Result<(), std::io::Error>. Isso quer dizer “ou
dá certo e não retorna nada ((), o tipo unit), ou dá errado e retorna um
std::io::Error”. Esse io::Error já vem prontinho da biblioteca padrão do
Rust, é o mesmo tipo de erro que você recebe ao mexer com arquivo, rede,
etc