Um pacote — duas entidades: mito ou realidade?
\n\nImagine: você escreveu em Rust uma biblioteca incrível para trabalhar com grandes dados. Ela é relâmpago, segura e elegante. Mas você precisa não apenas de uma API para outros desenvolvedores, mas também de um utilitário CLI pronto para testar a biblioteca ou usá-la em pipelines. Surge a pergunta clássica que reuniu milhares de visualizações no Stack Overflow: «Package with both a library and a binary?» (pacote contendo tanto uma biblioteca quanto um arquivo executável).
\n\nPara um iniciante em Rust, isso soa como um quebra-cabeça. Para um desenvolvedor experiente, é uma tarefa de otimização da estrutura do projeto e do desempenho. Neste artigo, não apenas responderemos a essa pergunta, mas o faremos com foco em otimização e desempenho — duas coisas pelas quais Rust é mais amado.
\n\nVamos analisar como organizar corretamente esse pacote, evitar duplicação de código, acelerar a compilação e tornar seu binário realmente rápido. Vamos lá!
\n\nEstrutura do pacote: Cargo.toml resolve tudo
\n\nEm Rust, um único pacote Cargo pode conter tanto uma biblioteca (lib) quanto vários binários (bin). Isso não é mágica, mas sim uma simples convenção de estrutura de pastas. Vamos ver um exemplo mínimo.
\n\nCriaremos um projeto:
\n\ncargo new my_tool --lib\n\nAgora modificaremos o arquivo Cargo.toml para indicar explicitamente que temos tanto uma biblioteca quanto um binário:
[package]\nname = "my_tool"\nversion = "0.1.0"\nedition = "2021"\n\n[lib]\nname = "my_tool_lib"\npath = "src/lib.rs"\n\n[[bin]]\nname = "my_tool"\npath = "src/main.rs"\n\n[dependencies]\n\n\nA estrutura de pastas ficará assim:
\n\nmy_tool/\n├── Cargo.toml\n├── src/\n│ ├── lib.rs // Biblioteca\n│ └── main.rs // Binário\n\n\nSimples, não é? Mas o diabo está nos detalhes. Se você simplesmente copiar o código da biblioteca para o main.rs, terá duplicação e problemas de desempenho. É preciso agir de forma mais inteligente.
\n\nAbordagem correta: o binário usa a biblioteca
\n\nO binário deve importar a biblioteca como um crate externo. Mesmo que estejam no mesmo pacote. Isso não apenas reduz a duplicação, mas também permite que o compilador Rust otimize melhor o código através da otimização entre crates (LTO).
\n\nExemplo de src/lib.rs:
pub fn calculate(data: &[u64]) -> u64 {\n data.iter().sum()\n}\n\n#[cfg(test)]\nmod tests {\n use super::*;\n\n #[test]\n fn test_calculate() {\n assert_eq!(calculate(&[1, 2, 3]), 6);\n }\n}\n\n\nExemplo de src/main.rs:
use my_tool_lib::calculate;\n\nfn main() {\n let numbers = vec![10, 20, 30];\n println!("Soma: {}", calculate(&numbers));\n}\n\n\nObserve: usamos my_tool_lib — este é o nome que definimos para a biblioteca em Cargo.toml. O compilador automaticamente os vinculará em um único binário, mas manterá a modularidade.
Otimização de desempenho: LTO, inline e benchmarks
\n\nAgora que a estrutura está pronta, vamos falar sobre o principal — como extrair o máximo do seu pacote. Afinal, se você está criando uma ferramenta para processamento de dados, cada microssegundo conta.
\n\n1. Ative a Link-Time Optimization (LTO)
\n\nLTO permite que o compilador otimize o código através dos limites dos crates. Sem LTO, as funções da biblioteca e do binário são otimizadas separadamente. Com LTO, como um todo único.
\n\nAdicione em Cargo.toml:
[profile.release]\nlto = true\ncodegen-units = 1\n\n\nIsso aumentará o tempo de compilação, mas tornará o binário visivelmente mais rápido. Para o site educacional pythonlib.ru é importante destacar: LTO é o padrão para builds de produção em Rust.
\n\n2. Use dicas de inline
\n\nSe houver funções criticamente importantes na biblioteca,