---
title: "Compilação Condicional em Rust: cfg, Features e Targets"
url: "https://rustlang.com.br/blog/compilacao-condicional-rust-cfg-features/"
markdown_url: "https://rustlang.com.br/blog/compilacao-condicional-rust-cfg-features.MD"
description: "Aprenda a usar compilação condicional em Rust com #[cfg], cfg!, Cargo features e targets para criar código multiplataforma e modular com segurança."
date: "2026-03-27"
author: "Equipe Rust Brasil"
---

# Compilação Condicional em Rust: cfg, Features e Targets

Aprenda a usar compilação condicional em Rust com #[cfg], cfg!, Cargo features e targets para criar código multiplataforma e modular com segurança.


## Introdução

Uma das maiores forças do Rust é a capacidade de escrever código que funciona em múltiplas plataformas sem sacrificar performance ou segurança. No centro dessa capacidade está a **compilação condicional** — o mecanismo que permite incluir ou excluir código durante a compilação com base no sistema operacional, arquitetura, features habilitadas ou flags personalizadas.

Diferente da abordagem do C/C++ com `#ifdef` e o pré-processador textual, o Rust integra a compilação condicional diretamente no sistema de tipos e no compilador, garantindo que o código condicional seja verificado sintaticamente mesmo quando não está sendo compilado. Isso elimina uma classe inteira de bugs comuns em projetos C/C++ — para uma comparação detalhada entre as linguagens, confira nosso artigo [Rust vs C++](/blog/rust-vs-cpp-2026/).

Neste guia, vamos explorar desde os fundamentos do atributo `#[cfg()]` até padrões avançados de design com Cargo features, passando por exemplos práticos de código multiplataforma.

## O Atributo #[cfg()] — Fundamentos

O atributo `#[cfg()]` é a ferramenta principal para compilação condicional em Rust. Ele pode ser aplicado a funções, módulos, structs, impls, expressões e praticamente qualquer item da linguagem.

### Condições por Sistema Operacional

```rust
#[cfg(target_os = "linux")]
fn obter_diretorio_config() -> &'static str {
    "~/.config/meu_app"
}

#[cfg(target_os = "windows")]
fn obter_diretorio_config() -> &'static str {
    "%APPDATA%\\meu_app"
}

#[cfg(target_os = "macos")]
fn obter_diretorio_config() -> &'static str {
    "~/Library/Application Support/meu_app"
}
```

### Condições por Arquitetura

```rust
#[cfg(target_arch = "x86_64")]
fn usar_simd_avx2(dados: &[f32]) -> Vec<f32> {
    // Implementação otimizada com AVX2
    dados.iter().map(|x| x * 2.0).collect()
}

#[cfg(target_arch = "aarch64")]
fn usar_simd_neon(dados: &[f32]) -> Vec<f32> {
    // Implementação otimizada com NEON (ARM)
    dados.iter().map(|x| x * 2.0).collect()
}
```

### Operadores Lógicos

O `#[cfg()]` suporta combinações com `all()`, `any()` e `not()`:

```rust
// Somente em Linux ou macOS (sistemas Unix-like)
#[cfg(any(target_os = "linux", target_os = "macos"))]
fn usar_unix_sockets() {
    println!("Unix sockets disponíveis!");
}

// Apenas em modo debug E em plataforma 64-bit
#[cfg(all(debug_assertions, target_pointer_width = "64"))]
fn debug_avancado() {
    println!("Debug avançado ativo em plataforma 64-bit");
}

// Em qualquer plataforma EXCETO Windows
#[cfg(not(target_os = "windows"))]
fn usar_sinais_posix() {
    println!("Sinais POSIX disponíveis");
}
```

## A Macro cfg! — Verificação em Runtime

Enquanto `#[cfg()]` remove código durante a compilação, a macro `cfg!()` retorna um `bool` em tempo de execução, permitindo branching condicional sem remover código:

```rust
fn main() {
    if cfg!(target_os = "linux") {
        println!("Rodando no Linux");
    } else if cfg!(target_os = "windows") {
        println!("Rodando no Windows");
    } else {
        println!("Rodando em outro SO");
    }

    // Útil para logging condicional
    if cfg!(debug_assertions) {
        println!("Modo debug: informações extras ativas");
    }
}
```

A diferença fundamental: com `#[cfg()]`, o código excluído nem é compilado. Com `cfg!()`, todo o código é compilado, mas o compilador pode otimizar os branches mortos. Use `#[cfg()]` quando o código condicional não compilaria na outra plataforma (por exemplo, chamadas a APIs específicas do SO).

## O Sistema de Features do Cargo

O sistema de features do [Cargo](/ecossistema/cargo/) é onde a compilação condicional realmente brilha em projetos Rust. Features permitem habilitar ou desabilitar funcionalidades opcionais em bibliotecas e aplicações.

### Definindo Features no Cargo.toml

```toml
[package]
name = "minha-lib"
version = "0.1.0"
edition = "2021"

[features]
default = ["json"]      # Features ativadas por padrão
json = ["dep:serde", "dep:serde_json"]
yaml = ["dep:serde", "dep:serde_yaml"]
logging = ["dep:tracing"]
full = ["json", "yaml", "logging"]  # Feature que agrupa outras

[dependencies]
serde = { version = "1", optional = true, features = ["derive"] }
serde_json = { version = "1", optional = true }
serde_yaml = { version = "0.9", optional = true }
tracing = { version = "0.1", optional = true }
```

Para um guia completo sobre serialização com serde, veja nosso [Guia Completo do Serde](/artigos/serde-guia-completo/).

### Usando Features no Código

```rust
// Módulo só existe quando a feature "json" está ativa
#[cfg(feature = "json")]
pub mod json {
    use serde::{Serialize, Deserialize};

    pub fn serializar<T: Serialize>(valor: &T) -> Result<String, serde_json::Error> {
        serde_json::to_string_pretty(valor)
    }

    pub fn deserializar<T: for<'de> Deserialize<'de>>(json: &str) -> Result<T, serde_json::Error> {
        serde_json::from_str(json)
    }
}

#[cfg(feature = "logging")]
pub fn inicializar_logging() {
    tracing_subscriber::fmt::init();
}

#[cfg(not(feature = "logging"))]
pub fn inicializar_logging() {
    // No-op quando logging não está habilitado
}
```

### Habilitando e Desabilitando Features

```bash
# Compilar com features padrão
cargo build

# Compilar sem features padrão
cargo build --no-default-features

# Compilar com features específicas
cargo build --features "json,logging"

# Compilar com todas as features
cargo build --all-features
```

Essa integração com o [sistema de ferramentas do Cargo](/artigos/cargo-ferramentas-essenciais/) torna o gerenciamento de features extremamente ergonômico.

## cfg_attr — Atributos Condicionais

O `cfg_attr` permite aplicar outros atributos condicionalmente:

```rust
// Derivar Serialize apenas quando a feature "json" está ativa
#[cfg_attr(feature = "json", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone)]
pub struct Config {
    pub nome: String,
    pub versao: u32,
    pub ativo: bool,
}

// Aplicar lint condicional
#[cfg_attr(not(debug_assertions), deny(unused_variables))]
fn processar_dados(entrada: &str) -> String {
    let debug_info = "info extra"; // Warning apenas em release
    entrada.to_uppercase()
}
```

## Compilação Cross-Platform na Prática

Um exemplo real de código multiplataforma que utiliza compilação condicional para I/O de arquivos:

```rust
use std::path::PathBuf;

pub fn diretorio_dados() -> PathBuf {
    #[cfg(target_os = "linux")]
    {
        let home = std::env::var("HOME").unwrap_or_default();
        PathBuf::from(home).join(".local/share/meu_app")
    }

    #[cfg(target_os = "macos")]
    {
        let home = std::env::var("HOME").unwrap_or_default();
        PathBuf::from(home).join("Library/Application Support/meu_app")
    }

    #[cfg(target_os = "windows")]
    {
        let appdata = std::env::var("APPDATA").unwrap_or_default();
        PathBuf::from(appdata).join("meu_app")
    }
}

// Dependências específicas por target no Cargo.toml:
// [target.'cfg(windows)'.dependencies]
// windows = { version = "0.52", features = ["Win32_Foundation"] }
//
// [target.'cfg(unix)'.dependencies]
// nix = { version = "0.28", features = ["signal"] }
```

Para projetos [embarcados com Rust](/blog/rust-embedded-embassy-iot-2026/), a compilação condicional é ainda mais importante, permitindo suportar diferentes microcontroladores com o mesmo codebase.

## Custom Flags com --cfg

Você pode definir flags personalizadas durante a compilação:

```bash
RUSTFLAGS='--cfg minha_flag' cargo build
```

```rust
#[cfg(minha_flag)]
fn funcionalidade_experimental() {
    println!("Feature experimental ativa!");
}
```

Isso é especialmente útil em [pipelines de CI/CD](/artigos/ci-cd-rust/) para habilitar comportamentos específicos de teste ou deploy.

## Testando com Features

```rust
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_funcionalidade_basica() {
        // Teste que sempre roda
        assert!(true);
    }

    #[test]
    #[cfg(feature = "json")]
    fn test_serializacao_json() {
        let config = Config {
            nome: "teste".to_string(),
            versao: 1,
            ativo: true,
        };
        let json = json::serializar(&config).unwrap();
        assert!(json.contains("teste"));
    }
}
```

Para um guia completo sobre testes em Rust, consulte nosso [tutorial sobre testes](/tutoriais/testes-rust/).

## Boas Práticas para Compilação Condicional

1. **Features devem ser aditivas**: Habilitar uma feature nunca deve quebrar funcionalidades existentes. Cada feature adiciona capacidade — nunca remove.

2. **Evite o "cfg hell"**: Muitas condições `#[cfg()]` aninhadas tornam o código difícil de manter. Considere abstrair plataformas em módulos separados.

3. **Teste todas as combinações**: Use `cargo test --all-features` e `cargo test --no-default-features` no seu CI para garantir que todas as combinações compilam.

4. **Documente suas features**: Liste features disponíveis e o que cada uma habilita no README e na documentação.

5. **Use `all-features` no docs.rs**: Adicione ao `Cargo.toml`:
   ```toml
   [package.metadata.docs.rs]
   all-features = true
   ```

## Comparação: Rust cfg vs C #ifdef

| Aspecto | Rust `#[cfg()]` | C `#ifdef` |
|---------|----------------|------------|
| Verificação sintática | ✅ Mesmo quando excluído | ❌ Texto é ignorado |
| Integração com tipos | ✅ Total | ❌ Pré-processador textual |
| Composabilidade | ✅ `all()`, `any()`, `not()` | ⚠️ `&&`, `\|\|`, `!` limitados |
| Segurança de memória | ✅ Garantida pelo compilador | ❌ Nenhuma |
| Integração com build system | ✅ Cargo features nativas | ⚠️ CMake/Make manual |

## Conclusão

A compilação condicional em Rust é significativamente mais segura e ergonômica do que em C/C++, graças à integração com o sistema de tipos e o Cargo. Dominar `#[cfg()]`, features e targets é essencial para escrever código Rust multiplataforma e modular.

Se você está começando com Rust, confira nosso guia [Como Aprender Rust em 2026](/blog/como-aprender-rust-2026/). Para quem quer explorar paralelismo e performance, não deixe de ler nosso artigo sobre [Rayon: Paralelismo de Dados sem Medo](/blog/rayon-paralelismo-dados-rust/).

Para entender como compilação condicional se encaixa no sistema de módulos do Rust, veja nosso artigo sobre o [Sistema de Módulos](/artigos/sistema-modulos/).

### Veja Também

- [Cargo: Ferramentas Essenciais](/artigos/cargo-ferramentas-essenciais/)
- [Iteradores em Rust](/artigos/iteradores-rust/)
- [Otimização de Performance](/artigos/otimizacao-performance/)
- [Rust em Produção](/blog/rust-em-producao/)

Se você programa em outras linguagens, veja como a compilação condicional se compara: <a href="https://golang.com.br/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'golang.com.br' })">Go usa build tags</a>, enquanto <a href="https://ziglang.com.br/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'ziglang.com.br' })">Zig oferece comptime</a> para metaprogramação em tempo de compilação. Já <a href="https://python.dev.br/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'python.dev.br' })">Python</a> resolve isso em runtime com verificações de plataforma.
