A estrutura de um projeto (3): a função que ninguém conseguia testar
Por Dorian Chávez · 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 declarapub. Se o projeto só temmain.rs, não há nada para importar [1]. - A saída é partir o programa: a lógica em
src/lib.rse ummain.rsenxuto 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.rsmerecem um teste. Essas vão para a biblioteca.
Neste artigo
- O que aconteceu na terça às 19:20?
- O que o Rust Book pede?
- Quais são cinco erros frequentes?
- Exercício: dá para cruzar a fronteira?
- Para levar
- O que vem depois?
- O template e o curso
- O que a Hábil faz?
- 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.lockO 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.mdVamos 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:
| Onde | O que enxerga | O que testa |
|---|---|---|
No final de servicio.rs, config.rs e reporte.rs | Tudo, inclusive o privado | Cada peça sozinha: url_publica, clasificar, os erros de config |
tests/integracion.rs | Só o pub da biblioteca | As peças juntas, contra um servidor HTTP local |
tests/binario.rs | O programa, como processo | O que ele imprime e com que código sai |
A documentação de servicio.rs | O pub | Que 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.00sSã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:
- 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. - Violação, com
src/reporte.rstal como está. O mesmo teste tem que não compilar, com o erroE0603, 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 sostienePor 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 thisO 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.
| Erro | O que o denuncia | Fonte |
|---|---|---|
1. Tudo em main.rs | E0432: unresolved import a partir de tests/ | [1][2] |
2. Ajudas em tests/common.rs | Uma suíte com running 0 tests | [1] |
3. mod.rs e arquivo com nome | E0761: file for module … found at both | [4] |
4. pub em tudo | Nenhum 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ó pacote | cannot 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: `featureedition2024is required, mais uma sugestão de acrescentarcargo-featuresaoCargo.toml. Não siga: atualize o Rust. Se ele já foi instalado comapt, remova-o comsudo apt remove cargo rustc; se não, o instalador dorustupavisa que já há um Rust em/usr/bin(medido). Depois rodesudo apt install build-essential curle o comando dorustup` [13][14]:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | shSem 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.shO 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 miaNão compila. A primeira linha do erro:
error[E0603]: function `url_publica` is privatePasso 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 miaO 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.shO 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.shAtençã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.
- O projeto tem
src/lib.rs? Se não, que função demain.rsvaleria a pena testar de fora e hoje não dá? - Do que é
pubna biblioteca, o que outro crate usa de verdade? O que ninguém usa de fora poderia ser privado oupub(crate)? - 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/bashdo sistema) e no Linux arm64 (contêinerubuntu: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 noCargo.lock. - Curso de Rust da Hábil: se módulos,
pubou 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
- 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
- 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
- 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
- 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
- The Rust Reference, «Visibility and privacy». https://doc.rust-lang.org/reference/visibility-and-privacy.html
- The Cargo Book, «Package Layout». https://doc.rust-lang.org/cargo/guide/project-layout.html
- The Cargo Book, «Cargo Targets»: «Binaries» e «Integration tests». https://doc.rust-lang.org/cargo/reference/cargo-targets.html
- The Cargo Book, «cargo test»: «Documentation tests» e
--locked. https://doc.rust-lang.org/cargo/commands/cargo-test.html - The Cargo Book, «Frequently Asked Questions»: «Why have Cargo.lock in version control?». https://doc.rust-lang.org/cargo/faq.html
- 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
- The Cargo Book, «SemVer Compatibility». https://doc.rust-lang.org/cargo/reference/semver.html
- reqwest 0.13.5, «Struct Error», docs.rs. https://docs.rs/reqwest/0.13.5/reqwest/struct.Error.html
- Steve Klabnik, Carol Nichols e Chris Krycho, «Installation» (1.1), The Rust Programming Language. https://doc.rust-lang.org/book/ch01-01-installation.html
- The Rust Project, «Install Rust», rust-lang.org. https://www.rust-lang.org/tools/install
- Launchpad (Canonical), pacote
rustcpublicado 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 - 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
- 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/
- 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/
- GitHub Docs, «Creating a template repository». https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-template-repository
- GitHub Docs, «Disabling and enabling a workflow». https://docs.github.com/en/actions/how-tos/manage-workflow-runs/disable-and-enable-workflows
- David Tolnay, «dtolnay/rust-toolchain», README, GitHub. https://github.com/dtolnay/rust-toolchain
A operação da empresa enfrenta esses desafios?
Prefere e-mail? Escreva para hola@habil.mx