Introdução
Bem-vindo a Packages and Crates. Este laboratório faz parte do Livro do Rust. Você pode praticar suas habilidades em Rust no LabEx.
Neste laboratório, abordaremos packages e crates, onde um crate é a menor unidade de código que o compilador Rust considera e pode ser um crate binário ou um crate de biblioteca, e um package é uma coleção de um ou mais crates que fornece um conjunto de funcionalidades.
Packages e Crates
As primeiras partes do sistema de módulos que abordaremos são packages e crates.
Um crate é a menor quantidade de código que o compilador Rust considera de cada vez. Mesmo que você execute rustc em vez de cargo e passe um único arquivo de código-fonte (como fizemos em "Escrevendo e Executando um Programa Rust"), o compilador considera esse arquivo como um crate. Crates podem conter módulos, e os módulos podem ser definidos em outros arquivos que são compilados com o crate, como veremos nas próximas seções.
Um crate pode vir em uma de duas formas: um crate binário ou um crate de biblioteca. Crates binários são programas que você pode compilar em um executável que você pode executar, como um programa de linha de comando ou um servidor. Cada um deve ter uma função chamada main que define o que acontece quando o executável é executado. Todos os crates que criamos até agora foram crates binários.
Crates de biblioteca não têm uma função main e não são compilados em um executável. Em vez disso, eles definem funcionalidades destinadas a serem compartilhadas com vários projetos. Por exemplo, o crate rand que usamos no Capítulo 2 fornece funcionalidade que gera números aleatórios. Na maioria das vezes, quando os Rustaceans dizem "crate", eles se referem a um crate de biblioteca, e usam "crate" de forma intercambiável com o conceito geral de programação de uma "biblioteca".
O crate root é um arquivo de origem de onde o compilador Rust começa e constitui o módulo raiz do seu crate (explicaremos os módulos em profundidade em "Definindo Módulos para Controlar Escopo e Privacidade").
Um package é um conjunto de um ou mais crates que fornece um conjunto de funcionalidades. Um package contém um arquivo Cargo.toml que descreve como construir esses crates. Cargo é, na verdade, um package que contém o crate binário para a ferramenta de linha de comando que você tem usado para construir seu código. O package Cargo também contém um crate de biblioteca do qual o crate binário depende. Outros projetos podem depender do crate de biblioteca Cargo para usar a mesma lógica que a ferramenta de linha de comando Cargo usa.
Um crate pode vir em uma de duas formas: um crate binário ou um crate de biblioteca. Um package pode conter quantos crates binários você quiser, mas no máximo apenas um crate de biblioteca. Um package deve conter pelo menos um crate, seja um crate de biblioteca ou binário.
Vamos analisar o que acontece quando criamos um package. Primeiro, inserimos o comando cargo new my-project:
$ cargo new my-project
Created binary (application) `my-project` package
$ ls my-project
Cargo.toml
src
$ ls my-project/src
main.rs
Depois de executarmos cargo new my-project, usamos ls para ver o que o Cargo cria. No diretório do projeto, há um arquivo Cargo.toml, nos dando um package. Há também um diretório src que contém main.rs. Abra Cargo.toml em seu editor de texto e observe que não há menção de src/main.rs. Cargo segue uma convenção de que src/main.rs é o crate root de um crate binário com o mesmo nome do package. Da mesma forma, Cargo sabe que, se o diretório do package contiver src/lib.rs, o package contém um crate de biblioteca com o mesmo nome do package, e src/lib.rs é seu crate root. Cargo passa os arquivos crate root para rustc para construir a biblioteca ou o binário.
Aqui, temos um package que contém apenas src/main.rs, o que significa que ele contém apenas um crate binário chamado my-project. Se um package contiver src/main.rs e src/lib.rs, ele terá dois crates: um binário e uma biblioteca, ambos com o mesmo nome do package. Um package pode ter vários crates binários, colocando arquivos no diretório src/bin: cada arquivo será um crate binário separado.
Folha de Dicas de Módulos
Antes de entrarmos nos detalhes de módulos e caminhos, aqui fornecemos uma referência rápida sobre como módulos, caminhos, a palavra-chave
usee a palavra-chavepubfuncionam no compilador e como a maioria dos desenvolvedores organiza seu código. Passaremos por exemplos de cada uma dessas regras ao longo deste capítulo, mas este é um ótimo lugar para consultar como um lembrete de como os módulos funcionam.
- Comece do crate root: Ao compilar um crate, o compilador primeiro procura no arquivo crate root (geralmente
src/lib.rspara um crate de biblioteca ousrc/main.rspara um crate binário) o código a ser compilado.- Declarando módulos: No arquivo crate root, você pode declarar novos módulos; digamos que você declare um módulo "garden" com
mod garden;. O compilador procurará o código do módulo nestes lugares:
- Inline, dentro de chaves que substituem o ponto e vírgula após
mod garden- No arquivo
src/garden.rs- No arquivo
src/garden/mod.rs- Declarando submódulos: Em qualquer arquivo diferente do crate root, você pode declarar submódulos. Por exemplo, você pode declarar
mod vegetables;emsrc/garden.rs. O compilador procurará o código do submódulo dentro do diretório nomeado para o módulo pai nestes lugares:
- Inline, diretamente após
mod vegetables, dentro de chaves em vez do ponto e vírgula- No arquivo
src/garden/vegetables.rs- No arquivo
src/garden/vegetables/mod.rs- Caminhos para código em módulos: Uma vez que um módulo faz parte do seu crate, você pode se referir ao código nesse módulo de qualquer outro lugar no mesmo crate, desde que as regras de privacidade permitam, usando o caminho para o código. Por exemplo, um tipo
Asparagusno módulo de vegetais do jardim seria encontrado emcrate::garden::vegetables::Asparagus.- Privado vs. público: O código dentro de um módulo é privado de seus módulos pai por padrão. Para tornar um módulo público, declare-o com
pub modem vez demod. Para tornar os itens dentro de um módulo público também públicos, usepubantes de suas declarações.- A palavra-chave use: Dentro de um escopo, a palavra-chave
usecria atalhos para itens para reduzir a repetição de caminhos longos. Em qualquer escopo que possa se referir acrate::garden::vegetables::Asparagus, você pode criar um atalho comuse crate::garden::vegetables::Asparagus;e, a partir daí, você só precisa escreverAsparaguspara usar esse tipo no escopo.Aqui, criamos um crate binário chamado
backyardque ilustra essas regras. O diretório do crate, também chamadobackyard, contém estes arquivos e diretórios:backyard ├── Cargo.lock ├── Cargo.toml └── src ├── garden │ └── vegetables.rs ├── garden.rs └── main.rsO arquivo crate root neste caso é
src/main.rs, e ele contém:use crate::garden::vegetables::Asparagus; pub mod garden; fn main() { let plant = Asparagus {}; println!("I'm growing {:?}!", plant); }A linha
pub mod garden;diz ao compilador para incluir o código que ele encontra emsrc/garden.rs, que é:pub mod vegetables;Aqui,
pub mod vegetables;significa que o código emsrc/garden/vegetables.rstambém está incluído. Esse código é:#[derive(Debug)] pub struct Asparagus {}Agora, vamos entrar nos detalhes dessas regras e demonstrá-las em ação!
Resumo
Parabéns! Você concluiu o laboratório Packages and Crates. Você pode praticar mais laboratórios no LabEx para aprimorar suas habilidades.