Arquitetura e backend16 min

A estrutura de um projeto (3): a função que ninguém conseguia testar

Por · fundador e CEO da Hábil ·

Toda a lógica em main.rs e os testes de tests/ não conseguem importá-la, nem com pub. O que vai em lib.rs, o que fica privado e um template que comprova.

A estrutura de um projeto · entrega 3 de 7 · Rust

Cenário: os personagens e a história são inventados. Os dados técnicos e as citações levam a fonte no final.

Em 1 minuto

  • Em Rust, um teste de tests/ é outro crate. Só pode usar o que uma biblioteca declara pub. Se o projeto só tem main.rs, não há nada para importar [1].
  • A saída é partir o programa: a lógica em src/lib.rs e um main.rs enxuto que só a chama [2][3].
  • Em Rust tudo é privado enquanto não se diz pub [3][5]. Não é um incômodo: é uma fronteira que o compilador vigia, e ela permite mudar o que está dentro sem quebrar ninguém.
  • Daqui sai um template que se clona e roda de primeira, com um script que demonstra essa fronteira ficando vermelho quando deve.
  • Para segunda-feira: procure no projeto quais funções de main.rs merecem um teste. Essas vão para a biblioteca.

Neste artigo

  1. O que aconteceu na terça às 19:20?
  2. O que o Rust Book pede?
  3. Quais são cinco erros frequentes?
  4. Exercício: dá para cruzar a fronteira?
  5. Para levar
  6. O que vem depois?
  7. O template e o curso
  8. O que a Hábil faz?
  9. Referências

O que aconteceu na terça às 19:20?

O Iván quis testar uma função de fora, e o compilador respondeu que não havia nada para importar.

Terça-feira, 19:20. Terceiro andar.

Terceira tarefa, a primeira em Rust. O verificador outra vez, o do Go e do Python, que consulta serviços por HTTP. O pedido: «Que o relatório não deixe ver os tokens das URLs».

O programa tinha sido escrito por alguém que já não estava. Tudo vivia num único arquivo:

src/main.rs      900 líneas
Cargo.toml
Cargo.lock

O Iván encontrou a função que montava a URL do relatório, perto da linha 600, e a corrigiu. Queria um teste que dissesse «o token não sai», daqueles que vivem em tests/. Criou tests/reporte.rs e escreveu, como no livro:

use revisor::url_publica;

cargo test. A resposta (recortada):

error[E0432]: unresolved import `revisor`
  = help: if you wanted to use a crate named `revisor`, use `cargo add revisor` to add it to your `Cargo.toml`

Um crate chamado revisor? O projeto dele se chamava assim. Pôs pub na função e rodou de novo. O mesmo erro, palavra por palavra. E a sugestão apontava para um pacote de fora; ele queria o dele.

Às 19:41 escreveu para a Lucía, a líder técnica.

— Pus pub e continua sem encontrar.

— Cadê a biblioteca? — respondeu ela.

— Não tenho. É um programa.

— Aí está. Um programa não exporta nada. Os testes de tests/ são outro crate, e outro crate só pode usar uma biblioteca. Amanhã partimos esse main.rs em dois.

O Iván olhou as 900 linhas. O compilador não estava dizendo que o código estava errado. Estava dizendo que não tinha porta.

O que o Rust Book pede?

Que a lógica viva numa biblioteca e que main.rs só a chame, porque só uma biblioteca pode ser importada de fora.

O capítulo de testes do Rust Book diz isso sem rodeios. Se um projeto só tem src/main.rs, não dá para criar testes de integração em tests/ que importem as funções dele, porque «Only library crates expose functions that other crates can use; binary crates are meant to be run on their own» [1]: os binários são feitos para rodar sozinhos.

E dá a saída no mesmo parágrafo: um src/main.rs simples que chama a lógica de src/lib.rs [1]. O capítulo 12 transforma isso em receita e deixa pouco para main: ler os argumentos, preparar a configuração, chamar a biblioteca e tratar o erro [2].

Medido num projeto de uma função: em main.rs, privada ou pub, o teste dá E0432; em lib.rs e pub, passa.

O template desta entrega, estructura-rust, é feito assim:

estructura-rust/
├── Cargo.toml · Cargo.lock
├── servicios.yaml           el ejemplo
├── src/
│   ├── main.rs              EL PROGRAMA: opciones, llamada, código de salida
│   ├── lib.rs               LA BIBLIOTECA: declara los módulos y qué es público
│   ├── servicio.rs          el vocabulario. Sin red ni archivos
│   ├── config.rs            lee y valida servicios.yaml
│   ├── revisar.rs           consulta muchos servicios a la vez
│   └── reporte.rs           tabla y JSON
├── tests/                   cada archivo es un crate aparte
│   ├── integracion.rs
│   ├── binario.rs
│   └── comun/mod.rs
├── herramientas/probar-frontera.sh
├── .github/workflows/verificar.yml
└── README.md · LICENSE · LICENSE-CONTENIDO.md

Vamos parte por parte, com a pergunta de sempre: que problema isso resolve hoje?

Por que há duas raízes em src/?

lib.rs é a biblioteca e main.rs é o programa: dois crates do mesmo pacote, e o Cargo encontra os dois sem que ninguém peça.

São os caminhos por convenção do Cargo: a biblioteca em src/lib.rs, o executável em src/main.rs e os testes de integração em tests/ [6]. Por isso o Cargo.toml não declara nenhum.

O interessante é como eles conversam. O programa é só mais um usuário da biblioteca, como um crate externo: «It can only use the public API» [3]. main.rs tem as mesmas permissões que tests/ e que qualquer projeto alheio, e o livro vê isso como vantagem: sendo autor e cliente ao mesmo tempo, a API sai mais bem desenhada [3].

O que faz main.rs, então?

O mínimo que um programa não pode delegar: ler as opções, chamar e decidir com que código termina.

No template, main.rs lê três opções, chama a biblioteca, imprime o relatório e sai com 0, 1 ou 2. Não decide o que é um serviço saudável. O que sobra se testa executando o programa inteiro: quando há testes de integração, o Cargo compila o programa antes e deixa o caminho dele em CARGO_BIN_EXE_<nombre> [7]. É assim que tests/binario.rs trabalha. O que está em main.rs não pode ser importado de fora; executado, sim.

O que significa que tudo seja privado?

Que nada é visível fora do módulo onde nasce, a não ser a partir dos submódulos dele, enquanto não levar pub; e o compilador faz isso ser cumprido.

O Rust Book diz isso com uma lista: em Rust, todos os itens (funções, métodos, structs, enums, módulos e constantes) «are private to parent modules by default» [3]. A referência da linguagem resume: «By default, everything is private», com duas exceções que não vêm ao caso aqui [5].

Por que assim? Para que se saiba quais partes podem mudar sem quebrar o código de fora, diz o livro [3]. E chama a API pública de uma biblioteca de «your contract with users of your crate» [3]: o contrato com quem a usa.

Há um meio-termo, pub(crate): visível em todo o crate atual [5]. Engana, porque um teste de tests/ é outro crate (medido: dá o mesmo erro que o privado).

No template, reporte.rs oferece duas funções públicas, tabla e json. A que limpa a URL, url_publica, é privada: é um detalhe de como o relatório é montado.

Então, como se testa url_publica?

De dentro: os testes unitários vivem no mesmo arquivo e enxergam o privado.

A convenção do livro é um módulo tests no final de cada arquivo de src/, marcado com #[cfg(test)] para que só seja compilado com cargo test [1]. É um submódulo filho do arquivo, e «items in child modules can use the items in their ancestor modules» [1]: um filho enxerga o privado dos ancestrais. Por isso «Rust’s privacy rules do allow you to test private functions» [1]. A divisão no template:

Então, como se testa url_publica?
OndeO que enxergaO que testa
No final de servicio.rs, config.rs e reporte.rsTudo, inclusive o privadoCada peça sozinha: url_publica, clasificar, os erros de config
tests/integracion.rsSó o pub da bibliotecaAs peças juntas, contra um servidor HTTP local
tests/binario.rsO programa, como processoO que ele imprime e com que código sai
A documentação de servicio.rsO pubQue o exemplo de clasificar continue verdadeiro

O último merece uma linha. O exemplo que está no comentário de clasificar também é um teste: cargo test extrai os exemplos da documentação da biblioteca e os executa [8]. Os do programa, não: medido com um exemplo falso de propósito, em main.rs ninguém o roda e tudo sai verde; em lib.rs, o mesmo exemplo fica vermelho. Mais um motivo para a lógica viver em lib.rs.

É assim que cargo test --locked aparece no template, só as linhas que importam (os caminhos, recortados; os tempos mudam):

     Running unittests src/lib.rs (…)
test result: ok. 28 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
     Running unittests src/main.rs (…)
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
     Running tests/binario.rs (…)
test result: ok. 7 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.33s
     Running tests/integracion.rs (…)
test result: ok. 13 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.13s
   Doc-tests revisor
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

São 49 testes em cinco blocos. Eles são contados ali, nas linhas test result, e não pelo código de saída: se um arquivo de tests/ não compila, o bloco dele nem aparece.

Por que tests/comun/mod.rs e não tests/comun.rs?

Porque cada arquivo da raiz de tests/ é uma suíte, e um de ajudas apareceria como uma suíte vazia em cada execução.

Os dois testes de integração compartilham um servidor HTTP de teste em 127.0.0.1. Se ele vivesse em tests/comun.rs, o Cargo o compilaria como mais uma suíte [1], e ele apareceria em cada execução com running 0 tests (medido). O livro mostra esse caso e diz que isso «is not what we wanted» [1]. A saída dele é a do template: uma subpasta, porque os arquivos das subpastas de tests/ não são compilados como crates à parte [1]. Cada teste a traz com mod comun;.

E os módulos de src/, sem mod.rs?

Um arquivo por módulo, com o nome do módulo. É o caminho que o livro chama de mais idiomático.

Cada linha pub mod reporte; de lib.rs diz ao compilador que procure src/reporte.rs. O Rust Book ensina esse caminho e reconhece outro mais antigo, src/reporte/mod.rs, que ainda funciona [4]. Qual convém, e o que acontece se forem misturados, está no erro 3.

O que revisar.rs cuida que um template não deve ensinar errado?

Um tempo limite que cobre a consulta inteira, um teto para o que se lê e erros que nunca repetem a URL.

O tempo limite abrange a consulta completa, do nome do servidor ao corpo, e do corpo se leem no máximo 64 KiB. Uma nuance medida: se o servidor de nomes não responde, o relatório sai a tempo, mas o programa só termina quando o sistema desiste (no Linux, uns 10 s). Os erros do reqwest, a biblioteca HTTP, «may include the full URL», avisa a documentação dele [12]; o template nunca usa o texto deles. Nem o dos erros de YAML, que repetem o valor (medido): informa o serviço, o campo e, se o YAML não puder ser lido, a linha e a coluna. Tiramos cada proteção numa cópia, uma por uma: as dez ficaram vermelhas. E as bordas (o código 400, --paralelo 0, 1 e 60.000 ms) têm o próprio teste, que fica vermelho se a borda se mover.

E onde vai a correção do Iván?

Na biblioteca, em reporte.rs, junto de quem a usa; e o teste, no final do mesmo arquivo.

Na quarta-feira, o Iván e a Lucía partiram o main.rs. A função da URL ficou em reporte.rs, privada, com o teste unitário ao lado. O teste que o Iván queria, «o token não sai», ficou em tests/, mas usa a função pública json. De fora se testa o que se promete; de dentro, como se cumpre.

Como saber que a fronteira existe?

Porque um script a cruza de propósito a cada mudança e exige que o compilador a rejeite pelo motivo exato.

herramientas/probar-frontera.sh trabalha numa cópia temporária. Planta tests/frontera.rs, um teste que chama revisor::reporte::url_publica, e o compila duas vezes sobre a mesma cópia. Entre as duas, só muda uma palavra de src/reporte.rs, pub:

  1. Controle, com pub fn url_publica. O teste tem que compilar e passar. Se não, o experimento está mal montado (a função mudou de nome, o teste plantado tem um erro) e o script para no vermelho.
  2. Violação, com src/reporte.rs tal como está. O mesmo teste tem que não compilar, com o erro E0603, no teste e não na biblioteca.

Esta é a saída real:

1/2 control: con «pub fn url_publica», una prueba de tests/ la llama
   ✅ compiló y pasó (1 de 1): la prueba sembrada está bien escrita
2/2 violación: la misma prueba, con src/reporte.rs tal como está
     error[E0603]: function `url_publica` is private
        --> tests/frontera.rs:6:27
   ✅ el compilador la rechazó: lo que no es pub no se ve desde tests/
✅ frontera: se sostiene

Por que o controle? Porque um experimento que só espera um vermelho também fica «verde» quando falha pelo motivo errado. Quebramos o script de quatro maneiras: com a função renomeada ou o teste plantado mal escrito, ele para em «el control NO pasó»; com outro código de erro esperado, diz que falhou «pero NO por la privacidad»; com a função pub, diz ❌ COMPILÓ. Com pub(crate), a fronteira se sustenta.

O CI do GitHub está escrito para rodar formatação, clippy, os testes e a fronteira com o Rust estável e com o 1.85.0, e a fronteira também no macOS com o /bin/bash do sistema, a cada mudança e toda segunda-feira. O template já está publicado no GitHub. Se ele passar 60 dias sem atividade, o GitHub desliga a execução das segundas [20].

Quais são cinco erros frequentes?

Os dois primeiros vêm de não saber o que tests/ enxerga; o terceiro, de misturar estilos; os dois últimos, de abrir demais: a API ou o projeto.

1. Tudo em main.rs, sem lib.rs

Funciona, mas de tests/ não dá para importar nada, nem com pub.

É o erro do Iván [1]. O capítulo 12 dá a razão de fundo: como não dá para testar a função main diretamente, tirar a lógica dela permite testá-la inteira, e o que sobra se revisa lendo [2]. Quem denuncia é o E0432, com uma sugestão de cargo add que aponta para outro lado (medido). Não é preciso outro pacote: é preciso a biblioteca do próprio projeto.

2. As ajudas de teste em tests/common.rs

O Cargo o compila como mais uma suíte, e ela sai vazia em cada execução.

O livro mostra: tests/common.rs aparece com running 0 tests, mesmo sem ter testes [1]. Não quebra nada, mas suja a contagem. A saída é a subpasta, tests/common/mod.rs [1].

3. Misturar mod.rs com arquivos que levam o nome do módulo

Para o mesmo módulo, o compilador para; para módulos diferentes, compila, mas confunde.

O Rust Book distingue dois casos. Usando os dois estilos para o mesmo módulo, «you’ll get a compiler error» [4]. Medido no template, com src/reporte.rs e src/reporte/mod.rs ao mesmo tempo:

error[E0761]: file for module `reporte` found at both "src/reporte.rs" and "src/reporte/mod.rs"

Em módulos diferentes compila, mas «might be confusing for people navigating your project» [4], e com mod.rs se acaba com muitos arquivos de mesmo nome abertos no editor [4]. Escolha um estilo; o template usa o que o livro chama de mais idiomático.

4. Pôr pub em tudo porque o compilador reclama

Cada pub aumenta o que os outros podem usar, e o compilador deixa de avisar se ele sobra.

Diante de um E0603, pergunte antes quem precisa dessa função. Se for um teste, ele vai no final do arquivo, como teste unitário [1]. Se for outro módulo do mesmo crate, pub(crate) basta [5].

Se a biblioteca for publicada ou outro pacote a usar, o custo está escrito no guia de compatibilidade do Cargo, que trata das versões novas de um pacote: «Major: renaming/moving/removing any public items» [11]. O template não é publicado (publish = false): o que é pub só é usado por main.rs e tests/, que se corrigem na mesma mudança. Mesmo assim, há um custo medido: o pub desliga o aviso de código morto. Com pub(crate) e sem uso, o compilador diz `warning: function url_publica is never used; com pub, fica calado. Por isso a fronteira do template fica vermelha se url_publica virar pub`.

5. Não versionar o Cargo.lock quando o CI precisa dele, ou montar um workspace antes da hora

O primeiro deixa sem o registro de com o que se testou; o segundo, com uma estrutura que ainda não tem para quê.

A FAQ do Cargo diz que o Cargo.lock registra o estado de uma compilação que funcionou, para repeti-la igual em outro momento e em outra máquina, e que assim o CI só falha por mudanças novas e não por fatores externos [9]. cargo new o versiona por padrão; a FAQ esclarece que depende do que o pacote precisa [9]. Entre os usos, cita um que o template faz com a linha 1.85.0 do CI: «Verifying a minimum-supported Rust version (MSRV) that is less than the latest version of a dependency supports» [9]. E avisa do limite: esse determinismo «can give a false sense of security», porque o Cargo.lock não afeta quem usa o pacote [9]. O template roda com --locked, que falha se o arquivo faltar ou se o cargo quiser mudá-lo [8]. Medido, sem Cargo.lock:

error: cannot create the lock file …/Cargo.lock because --locked was passed to prevent this

O outro extremo é um workspace no primeiro dia: um conjunto de pacotes que compartilham Cargo.lock e pasta de compilação [10], que o Rust Book apresenta para quando o projeto já cresceu e se quer parti-lo em vários crates [10]. Com um pacote, não há nada para compartilhar.

5. Não versionar o Cargo.lock quando o CI precisa dele, ou montar um workspace antes da hora
ErroO que o denunciaFonte
1. Tudo em main.rsE0432: unresolved import a partir de tests/[1][2]
2. Ajudas em tests/common.rsUma suíte com running 0 tests[1]
3. mod.rs e arquivo com nomeE0761: file for module … found at both[4]
4. pub em tudoNenhum aviso de never used; se publicar, cada retirada é uma mudança maior[3][11]
5. Sem Cargo.lock com um CI que precisa dele, ou um workspace com um só pacotecannot create the lock file com --locked; uma estrutura que nada usa[8][9][10]

Exercício: dá para cruzar a fronteira?

A ideia é tentar usar uma função privada a partir de tests/, ver quanto é preciso abrir para conseguir e ver como o script avisa que se abriu demais.

É preciso ter Rust 1.85 ou mais novo (cargo --version), instalado com rustup, mais Git e bash; uns 20 minutos, mais alguns minutos na primeira vez, enquanto as dependências compilam.

  • No Linux Mint 22, não use apt install cargo: o Mint 22 se baseia no Ubuntu 24.04 [16], que traz o Rust 1.75 [15], e a edição 2024 chegou com o 1.85 [18]. Medido com esse cargo: `feature edition2024 is required, mais uma sugestão de acrescentar cargo-features ao Cargo.toml. Não siga: atualize o Rust. Se ele já foi instalado com apt, remova-o com sudo apt remove cargo rustc; se não, o instalador do rustup avisa que já há um Rust em /usr/bin (medido). Depois rode sudo apt install build-essential curl e o comando do rustup` [13][14]:
  curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

Sem build-essential, sai `error: linker cc not found` (medido). A lição 0 do curso de Rust explica passo a passo. - No macOS, xcode-select --install e depois o mesmo comando do rustup [13]. - Com o rustup já instalado, rustup update stable. No Windows, use o WSL. - Ao terminar, abra outro terminal e confira que cargo --version diz 1.85 ou mais: no mesmo terminal, o cargo ainda não é encontrado ou continua sendo o antigo (medido).

Passo 1. Clone e confira que está tudo verde.

git clone https://github.com/HabilMX/estructura-rust
cd estructura-rust
cargo test --locked
bash herramientas/probar-frontera.sh

O que deve aparecer: cinco linhas test result: ok. que somam 49 testes e, no final do script, ✅ frontera: se sostiene.

Passo 2. Preveja antes de mexer. O próximo passo é escrever um teste em tests/ que chame url_publica, que é privada. Anote três coisas: compila assim? E se ela virar pub(crate)? E se virar pub?

Passo 3. Tente cruzar.

cat > tests/mia.rs <<'EOF'
#[test]
fn quita_el_token() {
    assert_eq!(
        revisor::reporte::url_publica("https://pagos.ejemplo.com/salud?token=x"),
        "https://pagos.ejemplo.com/salud"
    );
}
EOF
cargo test --locked --test mia

Não compila. A primeira linha do erro:

error[E0603]: function `url_publica` is private

Passo 4. Abra-a para o crate. Copie só a linha de sed do sistema em uso: a do macOS pede um argumento vazio depois de -i (se no Mac o sed for o do GNU, use a do Linux).

# En Linux:
sed -i 's/^fn url_publica(/pub(crate) fn url_publica(/' src/reporte.rs

# En macOS:
sed -i '' 's/^fn url_publica(/pub(crate) fn url_publica(/' src/reporte.rs

cargo test --locked --test mia

O mesmo E0603. pub(crate) abre a função para todo o crate da biblioteca, e o teste é outro crate.

Passo 5. Abra-a de vez.

# En Linux:
sed -i 's/^pub(crate) fn url_publica(/pub fn url_publica(/' src/reporte.rs

# En macOS:
sed -i '' 's/^pub(crate) fn url_publica(/pub fn url_publica(/' src/reporte.rs

cargo test --locked --test mia
bash herramientas/probar-frontera.sh

O teste passa (test result: ok. 1 passed). Mas o script fica vermelho:

❌ COMPILÓ: una prueba de tests/ pudo llamar a revisor::reporte::url_publica. La frontera no existe.

Deu para usá-la de fora, e com isso ela entrou no contrato. É o controle trabalhando.

Passo 6. Volte tudo ao que era.

git checkout -- . && git clean -fd
bash herramientas/probar-frontera.sh

Atenção: git clean -fd também apaga os arquivos novos criados ali, como anotações.

Como saber que deu certo: foram sete execuções e três desfechos. Verde, quatro vezes: as duas do passo 1, o teste do passo 5 e a fronteira do passo 6. Vermelho E0603, duas vezes: os passos 3 e 4. E o vermelho «COMPILÓ» do script no passo 5. Se no passo 4 o teste compilar, confira se não foi escrito pub em vez de pub(crate). Se no passo 5 o E0603 continuar, a linha de sed não encontrou pub(crate) fn url_publica(: revise o passo 4.

Para ir mais longe (opcional): mova tests/comun/mod.rs para tests/comun.rs (apague a pasta vazia) e rode cargo test --locked. Antes, preveja quantos blocos test result vão aparecer. Resposta: seis, um a mais que antes, Running tests/comun.rs com running 0 tests (medido). Volte com o mesmo git checkout -- . && git clean -fd.

Para levar

Três perguntas para fazer ao repositório esta semana.

  1. O projeto tem src/lib.rs? Se não, que função de main.rs valeria a pena testar de fora e hoje não dá?
  2. Do que é pub na biblioteca, o que outro crate usa de verdade? O que ninguém usa de fora poderia ser privado ou pub(crate)?
  3. O CI roda com --locked? Se não, com que versões das dependências foi testado o último verde?

O que vem depois?

No Go, a fronteira é vigiada pela ferramenta; no Python, pela instalação; no Rust, pelo compilador. No TypeScript há uma que o editor aceita e que o Node nunca vê.

Uma semana depois, o Iván herdou uma API em TypeScript. Um import com um alias bonito, @/revisar, compilava sem uma queixa. O editor o autocompletava.

O Node, ao arrancar, não o encontrou.

Como pode compilar um programa que não arranca? No domingo, 22 de novembro, na entrega 4: o que o tsconfig.json decide, o que não decide, e por que os testes e o código se compilam separadamente.

O template e o curso

  • Template: github.com/HabilMX/estructura-rust. Pode ser usado como template do GitHub ou clonado [19]. O README explica cada pasta, o que não traz, quando acrescentar cada coisa e como tirar o exemplo. O código é MIT; o texto e as explicações são CC BY-SA 4.0.
  • Versões: edição 2024 e rust-version = "1.85", a primeira com essa edição [18]. Foi testado com o Rust 1.99.1, o estável em 22 de outubro [17], no macOS arm64 (também com o /bin/bash do sistema) e no Linux arm64 (contêiner ubuntu:24.04), e com o 1.85.0 no macOS arm64. O Linux x86-64 ainda não foi testado. O CI está escrito para o estável de cada semana e o 1.85.0 no Linux, e o estável no macOS [21]. As dependências vão fixadas no Cargo.lock.
  • Curso de Rust da Hábil: se módulos, pub ou testes de integração forem novidade, a lição 6 explica tudo do zero: Curso de Rust · Lição 6, Módulos, testes e Cargo.

Escrevo a partir do que vemos todos os dias na Hábil, onde construímos sistemas para bancos, seguradoras e empresas no México. Se no trabalho ou na faculdade aparecer um repositório em que ninguém sabe onde mexer, vamos conversar: habil.mx

O que a Hábil faz?

Ensinamos a programar com o mesmo que usamos para construir: código testado, explicado e aberto.

Esta série nasce do mesmo lugar que os nossos cursos. O Curso de Rust é o segundo, depois de Go: ensina de quem é cada dado e que erros o compilador pega antes de executar, e o publicamos aberto, com licença Creative Commons; a lição 6 explica os módulos, os testes e o Cargo que o template deste artigo usa. E no catálogo há mais cursos de programação, gratuitos e com código testado e explicado passo a passo.

Veja o Curso de Rust e o catálogo de Cursos de programação.

Se a equipe herdar um sistema em que ninguém sabe onde mexer, ou precisar construir um que possa continuar sendo mantido, também fazemos isso: veja Construir um sistema sob medida. Peça a primeira conversa, sem custo. Nela avaliamos se convém configurar o que a equipe já tem, usar um produto ou construir, e o que ela receberia em cada caso. Escreva para nós pelo WhatsApp.

Fontes consultadas em 22 de outubro de 2026. As citações em inglês são reproduzidas tal como estão.

Referências

  1. Steve Klabnik, Carol Nichols e Chris Krycho, «Test Organization» (11.3), The Rust Programming Language. https://doc.rust-lang.org/book/ch11-03-test-organization.html
  2. Steve Klabnik, Carol Nichols e Chris Krycho, «Refactoring to Improve Modularity and Error Handling» (12.3): «Separation of Concerns for Binary Projects», The Rust Programming Language. https://doc.rust-lang.org/book/ch12-03-improving-error-handling-and-modularity.html
  3. Steve Klabnik, Carol Nichols e Chris Krycho, «Paths for Referring to an Item in the Module Tree» (7.3), incluída «Best Practices for Packages with a Binary and a Library», The Rust Programming Language. https://doc.rust-lang.org/book/ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html
  4. Steve Klabnik, Carol Nichols e Chris Krycho, «Separating Modules into Different Files» (7.5): «Alternate File Paths», The Rust Programming Language. https://doc.rust-lang.org/book/ch07-05-separating-modules-into-different-files.html
  5. The Rust Reference, «Visibility and privacy». https://doc.rust-lang.org/reference/visibility-and-privacy.html
  6. The Cargo Book, «Package Layout». https://doc.rust-lang.org/cargo/guide/project-layout.html
  7. The Cargo Book, «Cargo Targets»: «Binaries» e «Integration tests». https://doc.rust-lang.org/cargo/reference/cargo-targets.html
  8. The Cargo Book, «cargo test»: «Documentation tests» e --locked. https://doc.rust-lang.org/cargo/commands/cargo-test.html
  9. The Cargo Book, «Frequently Asked Questions»: «Why have Cargo.lock in version control?». https://doc.rust-lang.org/cargo/faq.html
  10. Steve Klabnik, Carol Nichols e Chris Krycho, «Cargo Workspaces» (14.3), The Rust Programming Language. https://doc.rust-lang.org/book/ch14-03-cargo-workspaces.html
  11. The Cargo Book, «SemVer Compatibility». https://doc.rust-lang.org/cargo/reference/semver.html
  12. reqwest 0.13.5, «Struct Error», docs.rs. https://docs.rs/reqwest/0.13.5/reqwest/struct.Error.html
  13. Steve Klabnik, Carol Nichols e Chris Krycho, «Installation» (1.1), The Rust Programming Language. https://doc.rust-lang.org/book/ch01-01-installation.html
  14. The Rust Project, «Install Rust», rust-lang.org. https://www.rust-lang.org/tools/install
  15. Launchpad (Canonical), pacote rustc publicado no Ubuntu 24.04 «noble»: 1.75.0. https://api.launchpad.net/1.0/ubuntu/+archive/primary?ws.op=getPublishedSources&source_name=rustc&exact_match=true&distro_series=https://api.launchpad.net/1.0/ubuntu/noble
  16. Linux Mint, «Linux Mint 22 Release Notes» («Linux Mint 22.x is based on Ubuntu 24.04»), linuxmint.com. https://www.linuxmint.com/rel_wilma.php
  17. The Rust Release Team, «Announcing Rust 1.99.1», Rust Blog, 15 de outubro de 2026. https://blog.rust-lang.org/2026/10/15/Rust-1.99.1/
  18. The Rust Release Team, «Announcing Rust 1.85.0 and Rust 2024», Rust Blog, 20 de fevereiro de 2025. https://blog.rust-lang.org/2025/02/20/Rust-1.85.0/
  19. GitHub Docs, «Creating a template repository». https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-template-repository
  20. GitHub Docs, «Disabling and enabling a workflow». https://docs.github.com/en/actions/how-tos/manage-workflow-runs/disable-and-enable-workflows
  21. David Tolnay, «dtolnay/rust-toolchain», README, GitHub. https://github.com/dtolnay/rust-toolchain