# Атрибуты и условная компиляция в Rust: #[cfg], #[inline], #[allow] и repr(C)

> Подробный разбор атрибутов и условной компиляции в Rust: внешние и внутренние атрибуты, #[cfg], макрос cfg!, оптимизации #[inline], линтер #[allow] и C-ABI разметка #[repr(C)].

- **URL:** https://eagle2000.space/rust/rust-attributes-and-cfg/
- **Date:** 2026-08-10

- **Tags:** rust, dev, attributes, cfg, inline, lints, repr-c, ffi

---
## Введение

Атрибуты в Rust — это метаданные, которые передаются компилятору `rustc` для управления сборкой, оптимизациями, условной компиляцией и бинарным представлением данных.

Если в прошлых статьях мы уже встречали макроатрибуты при написании макросов в [rust-procedural-macros](../rust-procedural-macros/) или при разметке структур для C-ABI в [rust-unsafe-and-ffi](../rust-unsafe-and-ffi/), то в этой статье мы системно разберем работу с атрибутами языка.

---

## 1. Анатомия атрибутов: внешние `#[...]` и внутренние `#![...]`

В Rust различают две категории атрибутов по их синтаксису и области применения:

1. **Внешние атрибуты (`#[...]`)**: относятся к элементу кода, расположенному непосредственно под ними (функция, структура, модуль, ветка перечисления).
2. **Внутренние атрибуты (`#![...]`)**: с восклицательным знаком относятся к **контейнеру**, внутри которого они объявлены (к текущему файлу, модулю или всему крейту).

```rust
#![allow(unused_imports)] // Внутренний атрибут: отключает предупреждение для ВСЕГО файла

#[allow(dead_code)] // Внешний атрибут: отключает предупреждение только для этой структуры
struct InternalConfig {
    id: u64,
}
```

---

## 2. Условная компиляция (`#[cfg]` и макрос `cfg!`)

Условная компиляция позволяет отсекать ненужные блоки кода до начала фазы оптимизаций и генерации машинных инструкций. Это незаменимо при написании кроссплатформенного кода под Linux, macOS и Windows.

Атрибут **`#[cfg(...)]`** поддерживает логические комбинаторы:
* `#[cfg(target_os = "linux")]` — проверка операционной системы.
* `#[cfg(not(target_os = "windows"))]` — логическое НЕ.
* `#[cfg(any(target_os = "linux", target_os = "macos"))]` — логическое ИЛИ.
* `#[cfg(all(unix, target_arch = "x86_64"))]` — логическое И.

Динамический макрос **`cfg!(...)`** позволяет проверять условия компиляции прямо внутри обычных ветвлений `if-else`.

Ниже представлен пошаговый пример работы с `#[cfg]` и макросом `cfg!`:


### 💻 Условная компиляция #[cfg] и макрос cfg!


#### Шаг 1

```rust
// Шаг 1: Условная компиляция функций с помощью атрибута #[cfg]

// Данная функция скомпилируется только на операционной системе Linux
#[cfg(target_os = "linux")]
fn get_os_family() -> &'static str {
    "Linux OS"
}

// Данная функция скомпилируется только на операционной системе macOS (darwin)
#[cfg(target_os = "macos")]
fn get_os_family() -> &'static str {
    "macOS (Darwin)"
}

// Данная функция скомпилируется только на операционной системе Windows
#[cfg(target_os = "windows")]
fn get_os_family() -> &'static str {
    "Windows OS"
}

// Резервная реализация для всех остальных систем
#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
fn get_os_family() -> &'static str {
    "Другая операционная система"
}

fn main() {
    println!("Текущая операционная система: {}", get_os_family());
}
```

**1. Условная компиляция #[cfg]**
- Атрибут `#[cfg(...)]` исключает не удовлетворяющий условию код прямо во время парсинга на этапе компиляции.
- Поддерживает логические операторы `any(...)`, `all(...)` и `not(...)`.


#### Шаг 2

```rust
// Шаг 2: Использование динамического макроса cfg!(...)
fn main() {
    // В отличие от атрибута #[cfg], макрос cfg! возвращает bool во время компиляции
    if cfg!(target_pointer_width = "64") {
        println!("Программа скомпилирована под 64-битную архитектуру");
    } else {
        println!("Программа скомпилирована под 32-битную архитектуру");
    }

    if cfg!(debug_assertions) {
        println!("Режим сборки: Debug");
    } else {
        println!("Режим сборки: Release (оптимизированный)");
    }
}
```

**2. Макрос cfg!(...)**
- Макрос `cfg!(...)` оставляет код скомпилированным, но вычисляет булево значение `true`/`false`.
- Позволяет ветвить логику внутри обычных `if`-выражений.


---

## 3. Оптимизации, линтеры и разметка памяти `#[repr(C)]`

Помимо условной компиляции, атрибуты управляют поведением компилятора и линтера:

- **`#[inline]` и `#[inline(always)]`**: дают подсказку компилятору встроить тело функции непосредственно в точку вызова, исключая накладные расходы на вызов.
- **`#[allow(...)]`, `#[warn(...)]`, `#[deny(...)]`**: настраивают уровни предупреждений встроенного линтера.
- **`#[repr(C)]`**: запрещает Rust-оптимизатору переупорядочивать поля структуры и фиксирует бинарную разметку по стандарту C-ABI.

Изучите пошаговый пример применения `#[inline]`, `#[allow]` и `#[repr(C)]` в блоке ниже:


### 💻 Атрибуты lints, inline и #[repr(C)]


#### Шаг 1

```rust
// Шаг 1: Управление линтером и оптимизациями (#[allow], #[inline])

// Подавляем предупреждения о неиспользуемом коде
#[allow(dead_code)]
struct UnusedConfig {
    debug_level: u32,
}

// Принудительное встраивание тела функции компилятором
#[inline(always)]
fn fast_add(a: i32, b: i32) -> i32 {
    a + b
}

fn main() {
    let result = fast_add(10, 20);
    println!("Результат быстрого сложения: {result}");
}
```

**1. Атрибуты lints и inline**
- `#[allow(...)]` подавляет выбранные предупреждения компилятора.
- `#[inline]` и `#[inline(always)]` подсказывают компилятору встроить тело функции в точку вызова для ускорения выполнения.


#### Шаг 2

```rust
// Шаг 2: Разметка памяти C-ABI через атрибут #[repr(C)]
#[repr(C)]
struct PointC {
    x: f64,
    y: f64,
}

fn main() {
    let point = PointC { x: 3.0, y: 4.0 };
    println!("Размер структуры с C-ABI разметкой: {} байт", std::mem::size_of::<PointC>());
    println!("Координаты: ({}, {})", point.x, point.y);
}
```

**2. Атрибут C-ABI разметки #[repr(C)]**
- По умолчанию Rust свободно переупорядочивает поля структуры для минимизации выравнивания и паддинга.
- `#[repr(C)]` фиксирует порядок и выравнивание полей по стандарту языка C, что критично для FFI и взаимодействия с C-библиотеками.


---

## Проверь свои знания!

Пройдите короткий тест по атрибутам и условной компиляции:


### 📝 Атрибуты и условная компиляция


**1. В чем главное отличие внешнего атрибута `#[...]` от внутреннего `#![...]`?**

- [ ] Внешний атрибут `#[...]` относится к элементу ниже, а внутренний `#![...]` относится к всему текущему файлу или крейту
- [ ] Внутренний атрибут используется только в асинхронных функциях
- [ ] Внешний атрибут отключает проверку типов

**2. Для чего используется атрибут условной компиляции `#[cfg(...)]`?**

- [ ] Для включения или исключения фрагментов кода на этапе компиляции в зависимости от ОС, архитектуры или фич
- [ ] Для автоматической отправки логов на сервер
- [ ] Для динамического связывания библиотек в runtime

**3. За что отвечает атрибут `#[inline(always)]`?**

- [ ] Подсказывает компилятору принудительно подставить тело функции непосредственно в место ее вызова
- [ ] Запрещает вызов функции из других модулей
- [ ] Создает глобальную переменную в куче

**4. Какая главная задача у атрибута `#[repr(C)]`?**

- [ ] Зафиксировать порядок и выравнивание полей структуры в соответствии со спецификацией ABI языка C
- [ ] Сжать структуру в памяти с помощью алгоритма zlib
- [ ] Преобразовать структуру в JSON-строку

