Aula 008: Traits e Generics em Rust
Nesta aula, vamos explorar duas características poderosas do Rust: Traits e Generics. Essas características nos permitem escrever código flexível e reutilizável. Vamos lá!
O que são Traits?
Traits em Rust são semelhantes a interfaces em outras linguagens de programação. Elas definem um conjunto de métodos que podem ser implementados por diferentes tipos. Isso permite que você especifique comportamentos compartilhados entre diferentes tipos sem conhecer suas implementações concretas.
Definindo um Trait
Vamos definir um trait simples chamado Falar
, que possui um método: falar
.
trait Falar {
fn falar(&self) -> String;
}
Implementando um Trait
Agora, vamos implementar o trait Falar
para diferentes tipos.
struct Cachorro;
struct Gato;
impl Falar for Cachorro {
fn falar(&self) -> String {
String::from("Au Au!")
}
}
impl Falar for Gato {
fn falar(&self) -> String {
String::from("Miau!")
}
}
Usando Traits
Agora que temos nosso trait definido e implementado, podemos escrever uma função que aceita qualquer tipo que implemente o trait Falar
.
fn deixar_o_animal_falar<T: Falar>(animal: T) {
println!("{}", animal.falar());
}
fn main() {
let cachorro = Cachorro;
let gato = Gato;
deixar_o_animal_falar(cachorro);
deixar_o_animal_falar(gato);
}
Quando você executar o código acima, verá a seguinte saída:
Au Au!
Miau!
O que são Generics?
Generics permitem que você escreva funções, structs e enums que podem operar em diferentes tipos de dados sem sacrificar a segurança de tipos. Em vez de especificar tipos concretos, você pode usar parâmetros de tipos.
Usando Generics em Funções
Vamos criar uma função simples que pode aceitar qualquer tipo e retornar seu comprimento.
fn obter_comprimento<T>(item: T) -> usize
where
T: std::ops::Deref<Target = str> {
item.len()
}
Neste caso, T
pode ser qualquer tipo que desreferencia para um slice de string.
Usando Generics em Structs
Você também pode definir structs que usam generics. Vamos definir uma struct de par simples.
struct Par<T, U> {
x: T,
y: U,
}
impl<T, U> Par<T, U> {
fn novo(x: T, y: U) -> Par<T, U> {
Par { x, y }
}
fn x(&self) -> &T {
&self.x
}
fn y(&self) -> &U {
&self.y
}
}
Usando Structs com Generics
Agora, vamos usar nossa struct Par
com diferentes tipos.
fn main() {
let inteiro_e_flotante = Par::novo(1, 2.5);
let string_e_char = Par::novo(String::from("Olá"), 'A');
println!("Inteiro: {}, Flotante: {}", inteiro_e_flotante.x(), inteiro_e_flotante.y());
println!("String: {}, Char: {}", string_e_char.x(), string_e_char.y());
}
Quando você executar o código acima, verá:
Inteiro: 1, Flotante: 2.5
String: Olá, Char: A
Conclusão
Nesta aula, aprendemos como definir e implementar traits e como usar generics em Rust. Utilizando essas características poderosas, podemos escrever código que seja ao mesmo tempo flexível e reutilizável. Traits nos permitem definir comportamentos compartilhados entre diferentes tipos, enquanto generics nos capacitam a escrever funções e structs que podem trabalhar com qualquer tipo.
À medida que você continua sua jornada em Rust, aproveitar o poder das traits e generics ajudará você a construir um código mais robusto e abstrato. Boas codificações!