Módulos
Os módulos são a forma em que o Rust tem para organizar o código, eles podem ser feitos no mesmo arquivo, em arquivos diferentes, ou em até subdiretórios do projeto.
Vamos pegar aquele primeiro exemplo utilizado nas structs e colocar ele dentro de um módulo, para declarar um módulo fazemos da seguinte maneira mod NomeDoModulo { conteúdo }
.
mod nota_fiscal { struct Cliente { nome: String, ano_de_nascimento: u16, documento: String, } impl Cliente { fn new(nome: String, ano_de_nascimento: u16, documento: String) -> Self { Self { nome, ano_de_nascimento, documento } } } } fn main() { let cliente = Cliente::new(String::from("Paulo"), 1999, String::from("Onde?")); }
Separamos a struct Cliente
em outro módulo, agora vamos tentar compilar este código... Espera temos um erro.
error[E0433]: failed to resolve: use of undeclared type `Cliente`
--> src/main.rs:20:23
|
20 | let mut cliente = Cliente::new(String::from("Paulo"), 1999, String::from("Onde?"));
| ^^^^^^^ not found in this scope
|
help: consider importing this struct
|
1 | use crate::nota_fiscal::Cliente;
|
For more information about this error, try `rustc --explain E0433`.
error: could not compile `modulos` due to previous error
A struct não foi encontrada no escopo. O compilador esta dizendo para importamos o cliente, vamos importar utilizando o que o compilador diz para importar use crate::nota_fiscal::Cliente;
mod nota_fiscal { struct Cliente { nome: String, ano_de_nascimento: u16, documento: String, } impl Cliente { fn new(nome: String, ano_de_nascimento: u16, documento: String) -> Self { Self { nome, ano_de_nascimento, documento } } } } //--declaração do modulo use crate::nota_fiscal::Cliente; fn main() { let cliente = Cliente::new(String::from("Paulo"), 1999, String::from("Onde?")); }
E compilamos... Outro erro.
error[E0603]: struct `Cliente` is private
--> src/main.rs:19:24
|
19 | use crate::nota_fiscal::Cliente;
| ^^^^^^^ private struct
|
note: the struct `Cliente` is defined here
--> src/main.rs:2:5
|
2 | struct Cliente {
| ^^^^^^^^^^^^^^
For more information about this error, try `rustc --explain E0603`.
error: could not compile `modulos` due to previous error
Por padrão todas as declarações dentro de um módulo são privadas, para resolvermos este problema utilizamos a palavra pub
, vamos adicionar isso tanto para a struct quanto para a implementação do método estático new
, porque ele também é privado.
mod nota_fiscal { pub struct Cliente { nome: String, ano_de_nascimento: u16, documento: String, } impl Cliente { pub fn new(nome: String, ano_de_nascimento: u16, documento: String) -> Self { Self { nome, ano_de_nascimento, documento } } } } //--método main use crate::nota_fiscal::Cliente; fn main() { let cliente = Cliente::new(String::from("Paulo"), 1999, String::from("Onde?")); println!("{} {} {}", cliente.nome, cliente.ano_de_nascimento, cliente.documento); }
Os atributos também são privados, caso tentarmos acessar qualquer um deles teremos outro erro de compilação, podemos acessar através de outros métodos públicos, ou deixando os atributos públicos.
mod nota_fiscal { pub struct Cliente { pub nome: String, pub ano_de_nascimento: u16, pub documento: String, } //--métodos impl Cliente { pub fn new(nome: String, ano_de_nascimento: u16, documento: String) -> Self { Self { nome, ano_de_nascimento, documento } } } } //--método main use crate::nota_fiscal::Cliente; fn main() { let cliente = Cliente::new(String::from("Paulo"), 1999, String::from("Onde?")); println!("{} {} {}", cliente.nome, cliente.ano_de_nascimento, cliente.documento); }
Separando módulos em outros arquivos
Não adianta muito criamos módulos para organizar o nosso código e mantermos tudo no mesmo arquivo. Vamos começar criando um arquivo nota_fiscal.rs
e jogando o código do Cliente para este arquivo.
A estrutura do nosso projeto fica da seguinte maneira.
.
├── Cargo.lock
├── Cargo.toml
└── src
├── main.rs
└── nota_fiscal.rs
main.rs
mod nota_fiscal; use crate::nota_fiscal::Cliente; fn main() { let cliente = Cliente::new(String::from("Paulo"), 1999, String::from("Onde?")); println!( "Nome: {}\nAno de nascimento: {}, Documento: {}", cliente.nome, cliente.ano_de_nascimento, cliente.documento ); }
nota_fiscal.rs
#![allow(unused)] fn main() { pub struct Cliente { pub nome: String, pub ano_de_nascimento: u16, pub documento: String, } impl Cliente { pub fn new(nome: String, ano_de_nascimento: u16, documento: String) -> Self { Self { nome, ano_de_nascimento, documento, } } } }
Agora o projeto dividido. Porém, esta não é a única maneira, podemos utilizar uma pasta com o mesmo nome nota_fiscal
e dentro da pasta um arquivo mod.rs
, para termos o mesmo efeito.
E teríamos a seguinte estrutura de projeto.
.
├── Cargo.lock
├── Cargo.toml
└── src
├── main.rs
└── nota_fiscal
└── mod.rs
O código de nota_fiscal.rs
é transferido para mod.rs
na pasta nota_fiscal
e nada mais é mudado. Temos o mesmo comportamento e o código dividido em módulos.
Importando e re-exportando módulos
Vamos realizar a criação de um módulo chamado pedido
dentro de nosso modulo nota_fiscal
e dentro do módulo pedido
vamos criar um módulo produto
.
A estrutura do nosso projeto ficara da seguinte maneira:
.
├── Cargo.lock
├── Cargo.toml
└── src
├── main.rs
└── nota_fiscal
├── mod.rs
└── pedido
└── mod.rs
nota_fiscal/mod.rs
#![allow(unused)] fn main() { pub mod pedido; #[derive(Debug)] pub struct Cliente { pub nome: String, pub ano_de_nascimento: u16, pub documento: String, } impl Cliente { pub fn new(nome: String, ano_de_nascimento: u16, documento: String) -> Self { Self { nome, ano_de_nascimento, documento } } } }
nota_fiscal/pedido/mod.rs
#![allow(unused)] fn main() { use self::produto::Produto; use super::Cliente; #[derive(Debug)] pub struct Pedido<'a> { pub cliente: Cliente, pub produtos: &'a [Produto] } impl<'a> Pedido<'a> { pub fn new(cliente: Cliente, produtos: &'a [Produto]) -> Self { Self { cliente, produtos } } } pub mod produto { #[derive(Debug)] pub struct Produto { pub nome: String, pub preco: f64 } impl Produto { pub fn new(nome: String, preco: f64) -> Self { Self { nome, preco } } } } }
main.rs
mod nota_fiscal; use nota_fiscal::pedido::{Pedido, produto::Produto}; use crate::nota_fiscal::Cliente; fn main() { let cliente = Cliente::new(String::from("Rust4Noobs"), 1999, String::from("Q?")); let produto = Produto::new(String::from("4Noobs"), 0f64); let produtos = &[produto]; let pedido = Pedido::new(cliente, produtos); println!("{:#?}", pedido) }
Nesse exemplo temos vários modos de imports, temos um impor com a palavra crate
que é a raiz do nosso projeto. Seria o modo de import do mesmo projeto com o path absoluto, temos também o super
que é um import a partir do modulo anterior, ou seja, o modulo que declara aquele módulo como tal. Meio confuso, mas conforme vamos praticando fica mais fácil de entender. E temos o import a partir de `nota_fiscal' sendo um modulo do nosso projeto, podemos importar tudo a partir dele, é um modulo que foi declarado em nosso main.
Futuramente iremos utilizar outro modo de projeto que ira utilizar o arquivo lib.rs
, onde também podemos declarar os módulos e remover isso do main.rs
, com esse arquivo podemos importar conforme o nome do projeto no Cargo.toml
lib.rs
#![allow(unused)] fn main() { pub mod nota_fiscal; }
main.rs
use modulos::nota_fiscal::pedido::{Pedido, produto::Produto}; use modulos::nota_fiscal::Cliente; fn main() { let cliente = Cliente::new(String::from("Rust4Noobs"), 1999, String::from("Q?")); let produto = Produto::new(String::from("4Noobs"), 0f64); let produtos = &[produto]; let pedido = Pedido::new(cliente, produtos); println!("{:#?}", pedido) }
Nota:
Quando declaramos um módulo interno e utilizamos o "use" como "pub" o modulo interno é exportado como se fosse parte do módulo que o declarou.