Pode parecer que os caminhos que escrevemos para chamar funções até agora são inconvenientemente longos e repetitivos. Por exemplo, na Listagem 7-7, se escolhemos o caminho absoluto ou relativo para a função adicionar_a_lista_de_espera
, sempre que quiséssemos chamar adicionar_a_lista_de_espera
, tínhamos que especificar frente_da_casa
e hospedagem
também. Felizmente, existe uma maneira de simplificar esse processo. Podemos trazer um caminho para um escopo uma vez e, em seguida, chamar os itens nesse caminho como se fossem itens locais com a palavra-chave use
.
Na Listagem 7-11, trazemos o módulo crate::frente_da_casa::hospedagem
para o escopo da função comer_no_restaurante
, portanto, só temos que especificar hospedagem::adicionar_a_lista_de_espera
para chamar a função adicionar_a_lista_de_espera
em comer_no_restaurante
.
Nome do arquivo: src/lib.rs
mod frente_da_casa {
pub mod hospedagem {
pub fn adicionar_a_lista_de_espera() {}
}
}
use crate::frente_da_casa::hospedagem;
pub fn comer_no_restaurante() {
hospedagem::adicionar_a_lista_de_espera();
hospedagem::adicionar_a_lista_de_espera();
hospedagem::adicionar_a_lista_de_espera();
}
Adicionar use
e um caminho em um escopo é semelhante a criar um link simbólico no sistema de arquivos. Ao adicionar use crate::frente_da_casa::hospedagem
na raiz do crate, hospedagem
agora é um nome válido nesse escopo, como se o módulo hospedagem
tivesse sido definido na raiz do crate. Os caminhos trazidos ao escopo use
também verificam a privacidade, como quaisquer outros caminhos.
Você também pode trazer um item para o escopo com use
e um caminho relativo. A Listagem 7-12 mostra como especificar um caminho relativo para obter o mesmo comportamento da Listagem 7-11.
Nome do arquivo: src/lib.rs
mod frente_da_casa {
pub mod hospedagem {
pub fn adicionar_a_lista_de_espera() {}
}
}
use self::frente_da_casa::hospedagem;
pub fn comer_no_restaurante() {
hospedagem::adicionar_a_lista_de_espera();
hospedagem::adicionar_a_lista_de_espera();
hospedagem::adicionar_a_lista_de_espera();
}
Criação de caminhos de uso idiomáticos
Na Listagem 7-11, você pode ter se perguntado por que especificamos use crate::frente_da_casa::hospedagem
e, em seguida, chamamos hospedagem::adicionar_a_lista_de_espera
em comer_no_restaurante
em vez de especificar o caminho use
até a função adicionar_a_lista_de_espera
para obter o mesmo resultado, como na Listagem 7-13.
Nome do arquivo: src/lib.rs
mod frente_da_casa {
pub mod hospedagem {
pub fn adicionar_a_lista_de_espera() {}
}
}
use crate::frente_da_casa::hospedagem::adicionar_a_lista_de_espera;
pub fn comer_no_restaurante() {
adicionar_a_lista_de_espera();
adicionar_a_lista_de_espera();
adicionar_a_lista_de_espera();
}
Embora as Listagens 7-11 e 7-13 realizem a mesma tarefa, a Listagem 7-11 é a maneira idiomática de trazer uma função para o escopo use
. Trazendo o módulo pai da função para o escopo com use
temos que especificar o módulo pai ao chamar a função torna claro que a função não é definida localmente enquanto ainda minimiza a repetição do caminho completo. O código na Listagem 7-13 não está claro sobre onde adicionar_a_lista_de_espera
está definido.
Por outro lado, ao trazer structs, enums e outros itens com use
, é idiomático especificar o caminho completo. A Listagem 7-14 mostra a maneira idiomática de trazer a estrutura HashMap
da biblioteca padrão para o escopo de um crate binário.
Nome do arquivo: src/main.rs
use std::collections::HashMap;
fn main() {
let mut map = HashMap::new();
map.insert(1, 2);
}
Não há nenhuma razão forte por trás desse idioma: é apenas a convenção que surgiu, e as pessoas se acostumaram a ler e escrever código Rust dessa maneira.
A exceção a esse idioma é se estivermos trazendo dois itens com o mesmo nome para o escopo com declarações use
, porque Rust não permite isso. A Listagem 7-15 mostra como trazer dois tipos Result
para o escopo que têm o mesmo nome, mas módulos pai diferentes e como fazer referência a eles.
Nome do arquivo: src/lib.rs
Como você pode ver, o uso dos módulos pai distingue os dois tipos Result
. Se, em vez disso, especificássemos use std::fmt::Result
e use std::io::Result
, teríamos dois tipos Result
no mesmo escopo e Rust não saberia a qual deles se referia quando usamos Result
.
Fornecimento de novos nomes com a palavra-chave as
Há outra solução para o problema de trazer dois tipos do mesmo nome para o mesmo escopo com use
: após o caminho, podemos especificar as
e um novo nome local, ou alias, para o tipo. A Listagem 7-16 mostra outra maneira de escrever o código na Listagem 7-15 renomeando um dos dois tipos Result
usando as
.
Nome do arquivo: src/lib.rs
Na segunda declaração use
, escolhemos o novo nome IoResult
para o tipo std::io::Result
, que não entrará em conflito com o Result
de std::fmt
que também incluímos no escopo. A Listagem 7-15 e a Listagem 7-16 são consideradas idiomáticas, então a escolha é sua!
Reexportando nomes com pub use
Quando colocamos um nome no escopo com a palavra-chave use
, o nome disponível no novo escopo é privado. Para permitir que o código que chama nosso código se refira a esse nome como se tivesse sido definido no escopo desse código, podemos combinar pub
e use
. Essa técnica é chamada de reexportação porque estamos trazendo um item para o escopo, mas também disponibilizando esse item para que outros tragam ao seu escopo.
A Listagem 7-17 mostra o código da Listagem 7-11 com use
no módulo raiz alterado para pub use
.
Nome do arquivo: src/lib.rs
mod frente_da_casa {
pub mod hospedagem {
pub fn adicionar_a_lista_de_espera() {}
}
}
pub use crate::frente_da_casa::hospedagem;
pub fn comer_no_restaurante() {
hospedagem::adicionar_a_lista_de_espera();
hospedagem::adicionar_a_lista_de_espera();
hospedagem::adicionar_a_lista_de_espera();
}
Ao usar pub use
, o código externo agora pode chamar a função adicionar_a_lista_de_espera
usando hospedagem::adicionar_a_lista_de_espera
. Se não tivéssemos especificado pub use
, a função comer_no_restaurante
poderia chamar hospedagem::adicionar_a_lista_de_espera
em seu escopo, mas o código externo não poderia aproveitar esse novo caminho.
A reexportação é útil quando a estrutura interna de seu código é diferente de como os programadores que chamam seu código pensariam sobre o domínio. Por exemplo, nesta metáfora do restaurante, as pessoas que dirigem o restaurante pensam em "frente da casa" e "nos fundos da casa". Mas os clientes que visitam um restaurante provavelmente não pensarão nas partes do restaurante nesses termos. Com pub use
, podemos escrever nosso código com uma estrutura, mas expor uma estrutura diferente. Isso torna nossa biblioteca bem organizada para programadores que trabalham na biblioteca e programadores que chamam a biblioteca.
Usando Pacotes Externos
No Capítulo 2, programamos um projeto de jogo de adivinhação em Rust que usava um pacote externo chamado rand
para obter números aleatórios. Para usar rand
em nosso projeto, adicionamos esta linha ao arquivo Cargo.toml:
Nome do arquivo: Cargo.toml
[dependencies]
rand = "0.5.5"
Adicionar rand
como uma dependência em Cargo.toml diz ao Cargo para baixar o pacote rand
e todas as dependências de crates.io e disponibilizar rand
para nosso projeto.
Então, para trazer as definições rand
para o escopo de nosso pacote, adicionamos uma linha use
começando com o nome do crate rand
, e listamos os itens que queríamos trazer para o escopo. Lembre-se de que na seção "Gerando um número aleatório" no Capítulo 2, colocamos o traço Rng
no escopo e chamamos a função rand::thread_rng
:
Membros da comunidade Rust disponibilizaram muitos pacotes em crates.io, e colocar qualquer um deles em seu pacote envolve as mesmas etapas: listá-los no arquivo Cargo.toml do seu pacote e usar use
para trazer itens de seus crates para o escopo.
Observe que a biblioteca padrão (std
) também é um crate externo ao nosso pacote. Como a biblioteca padrão é enviada com a linguagem Rust, não precisamos alterar Cargo.toml para incluir std
. Mas precisamos nos referir a ele use
para trazer itens de lá para o escopo de nosso pacote. Por exemplo, com HashMap
usaríamos esta linha:
Este é um caminho absoluto começando com std
, o nome do crate da biblioteca padrão.
Usando caminhos aninhados para limpar listas grandes de use
Se estivermos usando vários itens definidos na mesmo crate ou mesmo módulo, listar cada item em sua própria linha pode ocupar muito espaço vertical em nossos arquivos. Por exemplo, essas duas declarações use
que tivemos no Jogo de Adivinhação na Listagem 2-4 trazem itens do escopo std
:
Nome do arquivo: src/main.rs
Em vez disso, podemos usar caminhos aninhados para trazer os mesmos itens para o escopo em uma linha. Fazemos isso especificando a parte comum do caminho, seguida por dois pontos e, em seguida, colchetes ao redor de uma lista das partes dos caminhos que diferem, conforme mostrado na Listagem 7-18.
Nome do arquivo: src/main.rs
Em programas maiores, colocar muitos itens no escopo do mesmo crate ou módulo usando caminhos aninhados pode reduzir muito o número de declarações use
separadas necessárias!
Podemos usar um caminho aninhado em qualquer nível de um caminho, o que é útil ao combinar duas declarações use
que compartilham um subcaminho. Por exemplo, a Listagem 7-19 mostra duas declarações use
: uma que traz std::io
para o escopo e outra que traz std::io::Write
para o escopo.
Nome do arquivo: src/lib.rs
use std::io;
use std::io::Write;
A parte comum desses dois caminhos é std::io
, e esse é o primeiro caminho completo. Para mesclar esses dois caminhos em uma declaraçãouse
, podemos usar self
no caminho aninhado, conforme mostrado na Listagem 7-20.
Nome do arquivo: src/lib.rs
use std::io::{self, Write};
Esta linha traz std::io
e std::io::Write
em seu escopo.
O Operador Glob
Se quisermos trazer todos os itens públicos definidos em um caminho para o escopo, podemos especificar esse caminho seguido pelo operador glob *
:
Esta declaração use
traz todos os itens públicos definidos no escopo std::collections
atual. Tenha cuidado ao usar o operador glob! Glob pode tornar mais difícil saber quais nomes estão no escopo e onde um nome usado em seu programa foi definido.
O operador glob é frequentemente usado ao testar para trazer tudo em teste para o módulo tests
; falaremos sobre isso na seção “Como escrever testes” no Capítulo 11. Às vezes, o operador glob também é usado como parte do padrão de prelúdio: consulte a documentação da biblioteca padrão para obter mais informações sobre esse padrão.
Traduzido por Acervo Lima. O original pode ser acessado aqui.
0 comentários:
Postar um comentário