Rust: Funcional ou OOP? Escolhendo o estilo para o código ideal

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Introdução: Dois mundos em uma linguagem

\n\n

Quando você começa a estudar Rust, rapidamente percebe sua posição única. Esta linguagem não impõe uma única forma de pensar. Diferente de Java (onde tudo é objeto) ou Haskell (onde tudo é função), Rust oferece a liberdade de escolha. Mas com essa liberdade vem a responsabilidade: qual abordagem escolher — a funcional ou a orientada a objetos?

\n\n

Muitos desenvolvedores que vêm para Rust do mundo Python ou C++ instintivamente se voltam para a POO (Programação Orientada a Objetos). Eles buscam classes, herança e padrões familiares. Outros, inspirados pelo mundo Haskell ou Scala, tentam programar em Rust exclusivamente no estilo funcional, usando mônadas, imutabilidade e composição.

\n\n

A verdade, como sempre, está no meio. Rust é uma linguagem multiparadigma que permite aproveitar o melhor de ambos os mundos. Neste artigo, vamos explorar como usar efetivamente a abordagem funcional em Rust e quando aplicar padrões orientados a objetos. Você aprenderá a escrever código que não apenas funciona, mas também encanta pela sua elegância.

\n\n

Fundamentos de POO em Rust: Traits em vez de classes

\n\n

Vamos esclarecer desde já. Em Rust não existem classes no sentido clássico. Mas isso não significa que você não possa usar POO. Em vez de classes, usamos traits e structs. São eles que permitem implementar encapsulamento, polimorfismo e até mesmo uma certa semelhança com herança.

\n\n

Structs e métodos

\n\n

As structs são seus "objetos". Você define os dados e, em seguida, com impl, adiciona métodos a eles:

\n\n
struct Player {\n    name: String,\n    health: i32,\n    level: u32,\n}\n\nimpl Player {\n    // Método construtor\n    fn new(name: &str) -> Self {\n        Player {\n            name: name.to_string(),\n            health: 100,\n            level: 1,\n        }\n    }\n\n    // Método que altera o estado\n    fn take_damage(&mut self, damage: i32) {\n        self.health = (self.health - damage).max(0);\n    }\n\n    // Getter (método de acesso)\n    fn is_alive(&self) -> bool {\n        self.health > 0\n    }\n}\n
\n\n

Este é o estilo POO puro: temos estado (campos da struct) e comportamento (métodos). Rust garante o encapsulamento através do controle de visibilidade (modificador pub).

\n\n

Polimorfismo através de traits

\n\n

Em vez de herança de classes, Rust usa polimorfismo através de traits. Isso é mais poderoso e seguro do que a herança clássica, pois você não enfrenta o "problema do diamante" ou a herança acidental de comportamentos indesejados.

\n\n
trait Attackable {\n    fn attack(&self, target: &mut Player);\n}\n\nstruct Swordsman {\n    damage: i32,\n}\n\nimpl Attackable for Swordsman {\n    fn attack(&self, target: &mut Player) {\n        target.take_damage(self.damage);\n        println!("O espadachim causou {} de dano!", self.damage);\n    }\n}\n\nstruct Mage {\n    spell_power: i32,\n    mana_cost: i32,\n}\n\nimpl Attackable for Mage {\n    fn attack(&self, target: &mut Player) {\n        target.take_damage(self.spell_power * 2);\n        println!("O mago lançou uma bola de fogo com força {}!", self.spell_power * 2);\n    }\n}\n
\n\n

Agora você pode trabalhar com qualquer objeto que implemente o trait Attackable, sem se preocupar com seu tipo específico:

\n\n
fn perform_attack(attacker: &impl Attackable, target: &mut Player) {\n    attacker.attack(target);\n}\n
\n\n

Isso é chamado de "duck typing" turbinado ou polimorfismo estático. Rust verifica tudo em tempo de compilação, portanto você não terá erros em tempo de execução.

\n\n

Abordagem funcional: Iteradores e closures

\n\n

Agora vamos mudar

Blogs

Book Recommendations