Para mostrar ao Rust onde encontrar um item em uma árvore de módulo, usamos um caminho da mesma forma que usamos um caminho ao navegar em um sistema de arquivos. Se quisermos chamar uma função, precisamos saber seu caminho.
Um caminho pode assumir duas formas:
-
Um caminho absoluto começa a partir de uma raiz de crate usando um nome de crate ou um literal
crate
. -
Um caminho relativo começa a partir do módulo atual e usa
self
,super
ou um identificador no módulo atual.
Os caminhos absolutos e relativos são seguidos por um ou mais identificadores separados por dois pontos duplos (::
).
Voltemos ao exemplo da Listagem 7-1. Como chamamos a função adicionar_a_lista_de_espera
? Isso é o mesmo que perguntar: qual é o caminho da função adicionar_a_lista_de_espera
? Na Listagem 7-3, simplificamos um pouco nosso código removendo alguns dos módulos e funções. Mostraremos duas maneiras de chamar a função adicionar_a_lista_de_espera
a partir de uma nova função comer_no_restaurante
definida na raiz do crate. A função comer_no_restaurante
faz parte da API pública do nosso crate de biblioteca, então a marcamos com a palavra-chave pub
. Na seção “Expondo caminhos com a palavra-chave pub
”, entraremos em mais detalhes sobre pub
. Observe que este exemplo ainda não será compilado; vamos explicar o porquê daqui a pouco.
Nome do arquivo: src/lib.rs
Esse código não compila.
mod frente_da_casa {
mod hospedagem {
fn adicionar_a_lista_de_espera() {}
}
}
pub fn comer_no_restaurante() {
// Caminho absoluto
crate::frente_da_casa::hospedagem::adicionar_a_lista_de_espera();
// Caminho relativo
frente_da_casa::hospedagem::adicionar_a_lista_de_espera();
}
Na primeira vez que chamamos a função adicionar_a_lista_de_espera
em comer_no_restaurante
, usamos um caminho absoluto. A função adicionar_a_lista_de_espera
é definida no mesmo crate que comer_no_restaurante
, o que significa que podemos usar a palavra-chave crate
para iniciar um caminho absoluto.
Depois de crate
, incluímos cada um dos módulos sucessivos até chegarmos a adicionar_a_lista_de_espera
. Você pode imaginar um sistema de arquivos com a mesma estrutura, e nós especificaríamos o caminho /frente_da_casa/hospedagem/adicionar_a_lista_de_espera
para executar o programa adicionar_a_lista_de_espera
; usar o nome crate
para iniciar a partir da raiz do crate é como usar /
para iniciar a partir da raiz do sistema de arquivos em seu shell.
A segunda vez que chamar adicionar_a_lista_de_espera
em comer_no_restaurante
, usamos um caminho relativo. O caminho começa com frente_da_casa
, o nome do módulo definido no mesmo nível da árvore de módulos que comer_no_restaurante
. Aqui, o equivalente do sistema de arquivos seria o uso do caminho frente_da_casa/hospedagem/adicionar_a_lista_de_espera
. Começar com um nome significa que o caminho é relativo.
A escolha de usar um caminho relativo ou absoluto é uma decisão que você tomará com base em seu projeto. A decisão deve depender da probabilidade de você mover o código de definição do item separadamente ou junto com o código que usa o item. Por exemplo, se movermos o módulo frente_da_casa
e a função comer_no_restaurante
para um módulo denominado esperiencia_do_cliente
, precisaremos atualizar o caminho absoluto para adicionar_a_lista_de_espera
, mas o caminho relativo ainda será válido. No entanto, se movêssemos a função comer_no_restaurante
separadamente para um módulo denominado jantando
, o caminho absoluto para a chamada adicionar_a_lista_de_espera
permaneceria o mesmo, mas o caminho relativo precisaria ser atualizado. Nossa preferência é especificar caminhos absolutos porque é mais provável mover definições de código e chamadas de item independentemente umas das outras.
Vamos tentar compilar a Listagem 7-3 e descobrir por que ela ainda não compila! O erro que obtemos é mostrado na Listagem 7-4.
$ cargo build
Compiling restaurant v0.1.0 (file:///projects/restaurant)
error[E0603]: module `hospedagem` is private
--> src/lib.rs:9:28
|
9 | crate::frente_da_casa::hospedagem::adicionar_a_lista_de_espera();
| ^^^^^^^ private module
|
note: the module `hospedagem` is defined here
--> src/lib.rs:2:5
|
2 | mod hospedagem {
| ^^^^^^^^^^^
error[E0603]: module `hospedagem` is private
--> src/lib.rs:12:21
|
12 | frente_da_casa::hospedagem::adicionar_a_lista_de_espera();
| ^^^^^^^ private module
|
note: the module `hospedagem` is defined here
--> src/lib.rs:2:5
|
2 | mod hospedagem {
| ^^^^^^^^^^^
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0603`.
error: could not compile `restaurant`
To learn more, run the command again with --verbose.
As mensagens de erro indicam que o módulo hospedagem
é privado. Em outras palavras, temos os caminhos corretos para o módulo hospedagem
e a função adicionar_a_lista_de_espera
, mas Rust não nos deixa usá-los porque não tem acesso às seções privadas.
Módulos não são úteis apenas para organizar seu código. Eles também definem o limite de privacidade em Rust: a linha que encapsula os detalhes de implementação que o código externo não tem permissão para conhecer, chamar ou confiar. Portanto, se você quiser tornar um item como uma função ou estrutura privada, coloque-o em um módulo.
A forma como a privacidade funciona no Rust é que todos os itens (funções, métodos, estruturas, enums, módulos e constantes) são privados por padrão. Os itens em um módulo pai não podem usar os itens privados dentro dos módulos filhos, mas os itens nos módulos filhos podem usar os itens em seus módulos ancestrais. O motivo é que os módulos filhos envolvem e ocultam seus detalhes de implementação, mas os módulos filhos podem ver o contexto no qual estão definidos. Para continuar com a metáfora do restaurante, pense nas regras de privacidade como sendo o back office de um restaurante: o que acontece lá é privado para os clientes do restaurante, mas os gerentes de escritório podem ver e fazer tudo no restaurante em que operam.
Rust optou por fazer com que o sistema de módulo funcionasse dessa maneira, de modo que ocultar detalhes de implementação internos seja o padrão. Dessa forma, você sabe quais partes do código interno podem ser alteradas sem quebrar o código externo. Mas você pode expor partes internas do código dos módulos filhos para módulos ancestrais externos usando a palavra-chave pub
para tornar um item público.
Expondo caminhos com a palavra-chave pub
Voltemos ao erro na Listagem 7-4 que nos disse que o módulo hospedagem
é privado. Queremos que a função comer_no_restaurante
no módulo pai tenha acesso à função adicionar_a_lista_de_espera
no módulo filho, portanto, marcamos o módulo hospedagem
com a palavra-chave pub
, conforme mostrado na Listagem 7-5.
Nome do arquivo: src/lib.rs
Esse código não compila.
mod frente_da_casa {
pub mod hospedagem {
fn adicionar_a_lista_de_espera() {}
}
}
pub fn comer_no_restaurante() {
// Caminho absoluto
crate::frente_da_casa::hospedagem::adicionar_a_lista_de_espera();
// Caminho relativo
frente_da_casa::hospedagem::adicionar_a_lista_de_espera();
}
Infelizmente, o código na Listagem 7-5 ainda resulta em um erro, conforme mostrado na Listagem 7-6.
$ cargo build
Compiling restaurant v0.1.0 (file:///projects/restaurant)
error[E0603]: function `adicionar_a_lista_de_espera` is private
--> src/lib.rs:9:37
|
9 | crate::frente_da_casa::hospedagem::adicionar_a_lista_de_espera();
| ^^^^^^^^^^^^^^^ private function
|
note: the function `adicionar_a_lista_de_espera` is defined here
--> src/lib.rs:3:9
|
3 | fn adicionar_a_lista_de_espera() {}
| ^^^^^^^^^^^^^^^^^^^^
error[E0603]: function `adicionar_a_lista_de_espera` is private
--> src/lib.rs:12:30
|
12 | frente_da_casa::hospedagem::adicionar_a_lista_de_espera();
| ^^^^^^^^^^^^^^^ private function
|
note: the function `adicionar_a_lista_de_espera` is defined here
--> src/lib.rs:3:9
|
3 | fn adicionar_a_lista_de_espera() {}
| ^^^^^^^^^^^^^^^^^^^^
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0603`.
error: could not compile `restaurant`
To learn more, run the command again with --verbose.
O que aconteceu? Adicionar a palavra-chave pub
antes de mod hospedagem
torna o módulo público. Com essa mudança, se podemos acessar frente_da_casa
, podemos acessar hospedagem
. Mas o conteúdo de hospedagem
ainda é privado; tornar o módulo público não torna seu conteúdo público. A palavra-chave pub
em um módulo permite apenas que o código em seus módulos ancestrais se refira a ele.
Os erros na Listagem 7-6 dizem que a função adicionar_a_lista_de_espera
é privada. As regras de privacidade se aplicam a structs, enums, funções e métodos, bem como a módulos.
Também tornemos a função adicionar_a_lista_de_espera
pública adicionando a palavra-chave pub
antes de sua definição, como na Listagem 7-7.
Nome do arquivo: src/lib.rs
mod frente_da_casa {
pub mod hospedagem {
pub fn adicionar_a_lista_de_espera() {}
}
}
pub fn comer_no_restaurante() {
// Caminho absoluto
crate::frente_da_casa::hospedagem::adicionar_a_lista_de_espera();
// Caminho relativo
frente_da_casa::hospedagem::adicionar_a_lista_de_espera();
}
Agora o código será compilado! vamos dar uma olhada no caminho absoluto e relativo e verificar por que adicionar a palavra-chave pub
nos permite usar esses caminhos em adicionar_a_lista_de_espera
com relação às regras de privacidade.
No caminho absoluto, começamos com crate
a raiz da árvore de módulos do nosso crate. Em seguida, o módulo frente_da_casa
é definido na raiz do crate. O módulo frente_da_casa
não é público, mas como a função comer_no_restaurante
é definida no mesmo módulo que frente_da_casa
(ou seja, comer_no_restaurante
e frente_da_casa
são irmãos), podemos nos referir a frente_da_casa
a partir de comer_no_restaurante
. O próximo é o módulo hospedagem
marcado com pub
. Podemos acessar o módulo pai de hospedagem
, para que possamos acessar hospedagem
. Finalmente, a função adicionar_a_lista_de_espera
é marcada com pub
e podemos acessar seu módulo pai, então esta chamada de função funciona!
No caminho relativo, a lógica é a mesma que o caminho absoluto, exceto para a primeira etapa: em vez de começar na raiz do crate, o caminho começa em frente_da_casa
. O módulo frente_da_casa
é definido dentro do mesmo módulo que comer_no_restaurante
, portanto, o caminho relativo a partir do módulo no qual comer_no_restaurante
está definido funciona. Então, porque hospedagem
e adicionar_a_lista_de_espera
estão marcados com pub
, o resto do caminho funciona, e esta chamada de função é válida!
Iniciando caminhos relativos com super
Também podemos construir caminhos relativos que começam no módulo pai usando super
no início do caminho. É como iniciar um caminho de sistema de arquivos com a sintaxe ..
. Por que queremos fazer isso?
Considere o código na Listagem 7-8 que modela a situação em que um chef corrige um pedido incorreto e o apresenta pessoalmente ao cliente. A função consertar_pedido_incorreto
chama a função servir_pedido
especificando o caminho para servir_pedido
começar com super
:
Nome do arquivo: src/lib.rs
fn servir_pedido() {}
mod fundo_da_casa {
fn consertar_pedido_incorreto() {
preparar_pedido();
super::servir_pedido();
}
fn preparar_pedido() {}
}
A função consertar_pedido_incorreto
está no módulo fundo_da_casa
, então podemos usar super
para ir para o módulo pai de fundo_da_casa
, que neste caso é a raiz crate
. A partir daí, procuramos servir_pedido
e encontramos. Sucesso! Achamos que o módulo fundo_da_casa
e a função servir_pedido
provavelmente permanecerão na mesma relação um com o outro e serão movidos juntos se decidirmos reorganizar a árvore de módulos do crate. Portanto, usamos super
por isso, teremos menos lugares para atualizar o código no futuro, se esse código for movido para um módulo diferente.
Tornando Structs e Enums Públicos
Também podemos usar pub
para designar structs e enums como públicos, mas há alguns detalhes extras. Se usarmos pub
antes de uma definição de estrutura, tornamos a estrutura pública, mas os campos da estrutura ainda serão privados. Podemos tornar cada campo público ou não, caso a caso. Na Listagem 7-9, definimos uma estrutura fundo_da_casa::cafeDaManha
pública com um campo torada
público, mas um campo fruta_temporada
privado. Isso modela o caso de um restaurante onde o cliente pode escolher o tipo de pão que vem com a refeição, mas o chef decide qual fruta acompanhará a refeição com base na temporada e no estoque. As frutas disponíveis mudam rapidamente, então os clientes não podem escolher a fruta ou mesmo ver quais frutas irão receber.
Nome do arquivo: src/lib.rs
mod fundo_da_casa {
pub struct cafeDaManha {
pub torada: String,
fruta_temporada: String,
}
impl cafeDaManha {
pub fn verao(torada: &str) -> cafeDaManha {
cafeDaManha {
torada: String::from(torada),
fruta_temporada: String::from("Pessegos"),
}
}
}
}
pub fn comer_no_restaurante() {
// Peça um café da manhã no verão com torradas de centeio
let mut meal = fundo_da_casa::cafeDaManha::verao("Centeio");
// Mudar de ideia sobre o pão que gostaríamos
meal.torada = String::from("Trigo");
println!("Eu gostaria de torrada de {} por favor", meal.torada);
// A próxima linha não será compilada se descomentarmos; não foram permitidos
// para ver ou modificar as frutas da estação que vêm com a refeição
// meal.fruta_temporada = String::from("amoras");
}
Como o campo torada
na estrutura fundo_da_casa::cafeDaManha
é público, no comer_no_restaurante
podemos escrever e ler do campo torada
usando a notação de ponto. Observe que não podemos usar o campo fruta_temporada
em comer_no_restaurante
porque fruta_temporada
é privado. Tente remover o comentário da linha modificando o valor do campo fruta_temporada
para ver qual erro você obtém!
Além disso, observe que, por fundo_da_casa::cafeDaManha
ter um campo privado, a estrutura precisa fornecer uma função pública associada que constrói uma instância de cafeDaManha
(nós a nomeamos verao
aqui). Se cafeDaManha
não tivéssemos essa função, não poderíamos criar uma instância de cafeDaManha
no comer_no_restaurante
porque não poderíamos definir o valor do campo privado fruta_temporada
em comer_no_restaurante
.
Em contraste, se tornarmos um enum público, todas as suas variantes serão públicas. Precisamos apenas da palavra-chave pub
antes de enum
, conforme mostrado na Listagem 7-10.
Nome do arquivo: src/lib.rs
mod fundo_da_casa {
pub enum Appetizer {
Sopa,
Salada,
}
}
pub fn comer_no_restaurante() {
let pedido1 = fundo_da_casa::Appetizer::Sopa;
let pedido2 = fundo_da_casa::Appetizer::Salada;
}
Como tornamos o enum Appetizer
público, podemos usar as variantes Sopa
e Salada
em comer_no_restaurante
. Enums não são muito úteis, a menos que suas variantes sejam públicas; seria irritante ter que anotar todas as variantes de enum pub
em todos os casos, então o padrão para variantes de enum é ser público. As estruturas são frequentemente úteis sem que seus campos sejam públicos, portanto, os campos de estrutura seguem a regra geral de tudo ser privado por padrão, a menos que anotado com pub
.
Há mais uma situação envolvendo pub
que não cobrimos, e esse é nosso último recurso do sistema de módulo: a palavra-chave use
. Vamos cobrir use
por si só primeiro e, em seguida, mostraremos como combinar pub
e use
.
Traduzido por Acervo Lima. O original pode ser acessado aqui.
0 comentários:
Postar um comentário