<h2>Features em Rust: O Que São e Por Que Importam</h2>
<p>As features (características) em Rust são flags de compilação que permitem ativar ou desativar funcionalidades de uma crate. Pense nelas como interruptores que ligam ou desligam partes do seu código durante a compilação. Isso é especialmente útil quando você quer distribuir uma biblioteca com diferentes níveis de funcionalidade, reduzir tamanho de binário ou gerenciar dependências opcionais.</p>
<p>No arquivo <code>Cargo.toml</code>, você define features na seção <code>[features]</code>. Cada feature pode ativar dependências extras, modificar o comportamento da compilação ou incluir módulos específicos. Isso oferece controle fino sobre o que é compilado e com que proposito.</p>
<h2>Definindo e Usando Features no Cargo.toml</h2>
<h3>Estrutura Básica de Features</h3>
<p>O primeiro passo é declarar suas features no <code>Cargo.toml</code>. Aqui está um exemplo prático:</p>
<pre><code class="language-toml">[package]
name = "minha-lib"
version = "0.1.0"
edition = "2021"
[dependencies]
serde = { version = "1.0", optional = true }
tokio = { version = "1.0", optional = true, features = ["full"] }
[features]
default = ["serde-support"]
serde-support = ["serde"]
async-runtime = ["tokio"]
full = ["serde-support", "async-runtime"]</code></pre>
<p>No exemplo acima, <code>serde</code> e <code>tokio</code> são dependências opcionais. A feature <code>serde-support</code> ativa a dependência <code>serde</code>, enquanto <code>async-runtime</code> ativa <code>tokio</code>. A feature <code>full</code> ativa ambas. Por padrão, <code>serde-support</code> é sempre compilada.</p>
<h3>Ativando Features na Compilação</h3>
<p>Para compilar com features específicas:</p>
<pre><code class="language-bash">cargo build --features "serde-support"
cargo build --features "serde-support,async-runtime"
cargo build --all-features
cargo build --no-default-features</code></pre>
<p>No seu código Rust, use o atributo <code>#[cfg(feature = "nome")]</code> para compilar condicionalmente:</p>
<pre><code class="language-rust">#[cfg(feature = "serde-support")]
use serde::{Serialize, Deserialize};
pub struct Usuario {
id: u32,
nome: String,
}
#[cfg(feature = "serde-support")]
impl Serialize for Usuario {
// implementação
}
#[cfg(feature = "async-runtime")]
pub async fn buscar_dados() {
println!("Usando Tokio!");
}</code></pre>
<h2>Compilação Condicional em Rust</h2>
<h3>Atributos de Compilação Condicional</h3>
<p>Rust oferece vários atributos para controlar compilação além de features. Os principais são <code>#[cfg()]</code>, <code>#[cfg_attr()]</code> e a macro <code>cfg!()</code>.</p>
<pre><code class="language-rust">// Compilação específica para plataforma
#[cfg(target_os = "windows")]
fn obter_caminho() -> &'static str {
"C:\\"
}
#[cfg(target_os = "unix")]
fn obter_caminho() -> &'static str {
"/"
}
// Compilação em modo debug apenas
#[cfg(debug_assertions)]
fn debug_info() {
println!("Modo debug ativo");
}
// Compilação condicional com macro cfg!()
fn main() {
if cfg!(feature = "serde-support") {
println!("Serde está habilitado");
}
if cfg!(target_pointer_width = "64") {
println!("Sistema 64-bit");
}
}</code></pre>
<h3>Combinando Múltiplas Condições</h3>
<p>Você pode combinar múltiplas condições com <code>all()</code>, <code>any()</code> e <code>not()</code>:</p>
<pre><code class="language-rust">#[cfg(all(feature = "async-runtime", target_os = "linux"))]
pub async fn rotina_especifica_linux() {
println!("Apenas em Linux com async-runtime");
}
#[cfg(any(feature = "serde-support", feature = "json-support"))]
pub fn serializar() {
println!("Um dos suportes de serialização está ativo");
}
#[cfg(not(debug_assertions))]
pub fn otimizacoes_release() {
println!("Compilado em release");
}</code></pre>
<h2>Exemplo Prático: Uma Biblioteca com Features Múltiplas</h2>
<p>Vou demonstrar uma biblioteca completa que gerencia dados com suporte opcional a serialização e banco de dados:</p>
<pre><code class="language-rust">// src/lib.rs
use std::collections::HashMap;
#[cfg(feature = "serde-support")]
use serde::{Serialize, Deserialize};
#[cfg_attr(feature = "serde-support", derive(Serialize, Deserialize))]
#[derive(Debug, Clone)]
pub struct Produto {
pub id: u32,
pub nome: String,
pub preco: f64,
}
pub struct Catalogo {
produtos: HashMap<u32, Produto>,
}
impl Catalogo {
pub fn novo() -> Self {
Catalogo {
produtos: HashMap::new(),
}
}
pub fn adicionar(&mut self, produto: Produto) {
self.produtos.insert(produto.id, produto);
}
pub fn listar(&self) -> Vec<&Produto> {
self.produtos.values().collect()
}
#[cfg(feature = "serde-support")]
pub fn para_json(&self) -> Result<String, serde_json::Error> {
serde_json::to_string_pretty(&self.produtos)
}
#[cfg(feature = "banco-dados")]
pub async fn salvar_banco(&self) -> Result<(), Box<dyn std::error::Error>> {
println!("Salvando no banco de dados...");
Ok(())
}
}
#[cfg(test)]
mod testes {
use super::*;
#[test]
fn teste_catalogo() {
let mut cat = Catalogo::novo();
cat.adicionar(Produto {
id: 1,
nome: "Mouse".to_string(),
preco: 50.0,
});
assert_eq!(cat.listar().len(), 1);
}
}</code></pre>
<p>Seu <code>Cargo.toml</code>:</p>
<pre><code class="language-toml">[package]
name = "loja-lib"
version = "0.1.0"
edition = "2021"
[dependencies]
serde = { version = "1.0", optional = true, features = ["derive"] }
serde_json = { version = "1.0", optional = true }
[features]
default = ["serde-support"]
serde-support = ["serde", "serde_json"]
banco-dados = []</code></pre>
<p>Compilação:</p>
<pre><code class="language-bash">cargo build --features "serde-support"
cargo build --no-default-features
cargo test --all-features</code></pre>
<h2>Conclusão</h2>
<p>Dominando features e compilação condicional, você ganha três superpoderes em Rust: <strong>flexibilidade na distribuição</strong> de bibliotecas (ativar apenas o necessário), <strong>otimização de tamanho</strong> (remover código não utilizado em release), e <strong>controle multiplataforma</strong> (adaptar código para diferentes sistemas). Use features para dependências opcionais grandes, e <code>#[cfg()]</code> para pequenas variações de comportamento. Combine-as estrategicamente para criar bibliotecas profissionais e eficientes.</p>
<h2>Referências</h2>
<ul>
<li><a href="https://doc.rust-lang.org/cargo/reference/features.html" target="_blank" rel="noopener noreferrer">The Rust Book - Features</a></li>
<li><a href="https://doc.rust-lang.org/cargo/reference/manifest.html" target="_blank" rel="noopener noreferrer">Cargo Manifest Documentation</a></li>
<li><a href="https://doc.rust-lang.org/reference/conditional-compilation.html" target="_blank" rel="noopener noreferrer">Conditional Compilation - Rust Reference</a></li>
<li><a href="https://github.com/rust-lang/rustlings" target="_blank" rel="noopener noreferrer">Rustlings - Features Exercise</a></li>
<li><a href="https://www.lurklurk.org/effective-rust/" target="_blank" rel="noopener noreferrer">Effective Rust - Feature Flags</a></li>
</ul>