Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Внешние блоки

Syntax
ExternBlock
    unsafe?1 extern Abi? {
        InnerAttribute*
        ExternalItem*
    }

ExternalItem
    OuterAttribute* (
        MacroInvocationSemi
      | Visibility? StaticItem
      | Visibility? Function
    )

Внешние блоки предоставляют объявления элементов, которые не определены в текущем крейте и являются основой внешнего функционального интерфейса Rust. Они аналогичны непроверенным импортам.

Два вида объявлений элементов разрешены во внешних блоках: функции и статические переменные.

Вызов небезопасных функций или доступ к небезопасным статическим переменным, объявленным во внешних блоках, разрешён только в небезопасном контексте.

Внешний блок определяет свои функции и статические переменные в пространстве имён значений модуля или блока, где он находится.

Ключевое слово unsafe семантически требуется появляться перед ключевым словом extern во внешних блоках.

2024 Edition differences

До редакции 2024 ключевое слово unsafe было опциональным. Квалификаторы элементов safe и unsafe разрешены только если сам внешний блок помечен как unsafe.

Функции

Функции внутри внешних блоков объявляются так же, как и другие Rust функции, за исключением того, что они не должны иметь тела и вместо этого завершаются точкой с запятой.

Образцы не разрешены в параметрах, могут использоваться только IDENTIFIER или _.

Квалификаторы функций safe и unsafe разрешены, но другие квалификаторы функций (например, const, async, extern) не разрешены.

Функции внутри внешних блоков могут вызываться кодом Rust, так же как функции, определённые в Rust. Компилятор Rust автоматически преобразует между ABI Rust и иностранным ABI.

Функция, объявленная во внешнем блоке, неявно является unsafe, если не присутствует квалификатор функции safe.

При приведении к указателю на функцию, функция, объявленная во внешнем блоке, имеет тип extern "abi" for<'l1, ..., 'lm> fn(A1, ..., An) -> R, где 'l1, … 'lm - это её параметры времени жизни, A1, …, An - объявленные типы её параметров, R - объявленный тип возвращаемого значения.

Статические переменные

Статические переменные внутри внешних блоков объявляются так же, как статические переменные вне внешних блоков, за исключением того, что у них нет выражения, инициализирующего их значение.

Если статический элемент, объявленный во внешнем блоке, не квалифицирован как safe, то доступ к этому элементу является unsafe, независимо от того, является ли он изменяемым, потому что нет ничего, гарантирующего, что битовый шаблон в памяти статической переменной действителен для типа, с которым она объявлена, поскольку некоторый произвольный (например, C) код отвечает за инициализацию статической переменной.

Внешние статические переменные могут быть как неизменяемыми, так и изменяемыми, так же как статические переменные вне внешних блоков.

Неизменяемая статическая переменная должна быть инициализирована до выполнения любого кода Rust. Недостаточно, чтобы статическая переменная была инициализирована до того, как код Rust прочитает её. Как только код Rust запускается, изменение неизменяемой статической переменной (изнутри или снаружи Rust) является неопределённым поведением, за исключением случаев, когда изменение происходит с байтами внутри UnsafeCell.

ABI

Ключевое слово extern может сопровождаться опциональной строкой ABI. ABI определяет соглашение о вызовах функций в блоке. Соглашение о вызовах определяет низкоуровневый интерфейс для функций, такой как размещение аргументов в регистрах или стеке, передача возвращаемых значений и ответственность за очистку стека.

Example

#![allow(unused)]
fn main() {
// Интерфейс к Windows API.
unsafe extern "system" { /* ... */ }
}

Если строка ABI не указана, по умолчанию используется "C".

Note

Синтаксис extern без явного ABI постепенно устаревает, поэтому лучше всегда явно указывать ABI.

Для получения более подробной информации см. Rust issue #134986.

Следующие строки ABI поддерживаются на всех платформах:

  • unsafe extern "Rust" — Нативное соглашение о вызовах для функций и замыканий Rust. Это значение по умолчанию, когда функция объявлена без использования extern fn. ABI Rust не предлагает гарантий стабильности.
  • unsafe extern "C" — ABI “C” соответствует ABI по умолчанию, выбранному доминирующим компилятором C для цели.
  • unsafe extern "system" — Это эквивалентно extern "C", кроме Windows x86_32, где это эквивалентно "stdcall".

    Note

    Поскольку правильный базовый ABI на Windows зависит от цели, лучше использовать extern "system" при попытке связать функции Windows API, которые не используют явно определённый ABI.

  • extern "C-unwind" и extern "system-unwind" — Идентичны "C" и "system" соответственно, но с разным поведением, когда вызываемая функция раскручивает стек (паникуя или выбрасывая исключение в стиле C++).

Также есть некоторые специфичные для платформы строки ABI:

  • unsafe extern "aapcs" — ABI с программной эмуляцией чисел с плавающей точкой для ARM.

    • Доступно только для целей ARM32.
    • “aapcs” такой же, как ABI “C” на ARM32 с программной эмуляцией чисел с плавающей точкой.
    • Соответствует __attribute__((pcs("aapcs"))) в clang.

    Note

    Для подробностей см.:

  • unsafe extern "efiapi" — ABI, используемый для функций UEFI.
    • Доступно только для целей x86 и ARM (32-битные и 64-битные).

Как "C" и "system", большинство специфичных для платформы строк ABI также имеют соответствующий вариант -unwind; конкретно, это:

  • "aapcs-unwind"
  • "cdecl-unwind"
  • "fastcall-unwind"
  • "stdcall-unwind"
  • "sysv64-unwind"
  • "thiscall-unwind"
  • "win64-unwind"

Вариадические функции

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

#![allow(unused)]
fn main() {
unsafe extern "C" {
    unsafe fn foo(...);
    unsafe fn bar(x: i32, ...);
    unsafe fn with_name(format: *const u8, args: ...);
    // БЕЗОПАСНОСТЬ: Эта функция гарантирует, что не будет обращаться к
    // вариадическим аргументам.
    safe fn ignores_variadic_arguments(x: i32, ...);
}
}

Warning

Квалификатор safe не должен использоваться для функции во внешнем блоке, если эта функция не гарантирует, что она вообще не будет обращаться к вариадическим аргументам. Передача неожиданного количества аргументов или аргументов неожиданного типа в вариадическую функцию может привести к неопределённому поведению.

Вариадические параметры могут быть указаны только внутри внешних блоков со следующими строками ABI или их соответствующими вариантами -unwind:

  • "aapcs"
  • "C"
  • "cdecl"
  • "efiapi"
  • "sysv64"
  • "win64"

Атрибуты на внешних блоках

Следующие атрибуты управляют поведением внешних блоков.

Атрибут link указывает имя нативной библиотеки, с которой компилятор должен линковаться для элементов внутри блока extern.

Он использует синтаксис MetaListNameValueStr для указания своих входных данных. Ключ name - это имя нативной библиотеки для линковки. Ключ kind - это опциональное значение, которое указывает тип библиотеки со следующими возможными значениями:

  • dylib — Указывает динамическую библиотеку. Это значение по умолчанию, если kind не указан.
  • static — Указывает статическую библиотеку.
  • framework — Указывает фреймворк macOS. Это действительно только для целей macOS.
  • raw-dylib — Указывает динамическую библиотеку, для которой компилятор сгенерирует библиотеку импорта для линковки (см. dylib versus raw-dylib ниже для подробностей). Это действительно только для целей Windows.

Ключ name должен быть включён, если указан kind.

Опциональный аргумент modifiers - это способ указания модификаторов линковки для библиотеки для линковки.

Модификаторы указываются как строка, разделённая запятыми, с каждым модификатором, предваренным либо +, либо -, чтобы указать, что модификатор включён или выключен, соответственно.

Указание нескольких аргументов modifiers в одном атрибуте link, или нескольких идентичных модификаторов в том же аргументе modifiers в настоящее время не поддерживается.
Пример: #[link(name = "mylib", kind = "static", modifiers = "+whole-archive")].

Ключ wasm_import_module может использоваться для указания имени модуля WebAssembly для элементов внутри блока extern при импорте символов из хост-окружения. Имя модуля по умолчанию - env, если wasm_import_module не указан.

#[link(name = "crypto")]
unsafe extern {
    // …
}

#[link(name = "CoreFoundation", kind = "framework")]
unsafe extern {
    // …
}

#[link(wasm_import_module = "foo")]
unsafe extern {
    // …
}

Допустимо добавлять атрибут link на пустой внешний блок. Вы можете использовать это для удовлетворения требований линковки внешних блоков в другом месте вашего кода (включая вышестоящие крейты) вместо добавления атрибута к каждому внешнему блоку.

Модификаторы линковки: bundle

Этот модификатор совместим только с типом линковки static. Использование любого другого типа приведёт к ошибке компилятора.

При сборке rlib или staticlib +bundle означает, что нативная статическая библиотека будет упакована в архив rlib или staticlib, а затем извлечена оттуда во время линковки финального бинарника.

При сборке rlib -bundle означает, что нативная статическая библиотека регистрируется как зависимость этого rlib “по имени”, и объектные файлы из неё включаются только во время линковки финального бинарника, поиск файла по этому имени также выполняется во время финальной линковки.
При сборке staticlib -bundle означает, что нативная статическая библиотека просто не включается в архив, и некоторая система сборки более высокого уровня должна будет добавить её позже во время линковки финального бинарника.

Этот модификатор не имеет эффекта при сборке других целей, таких как исполняемые файлы или динамические библиотеки.

Значение по умолчанию для этого модификатора - +bundle.

Более подробная информация о реализации этого модификатора может быть найдена в документации bundle для rustc.

Модификаторы линковки: whole-archive

Этот модификатор совместим только с типом линковки static. Использование любого другого типа приведёт к ошибке компилятора.

+whole-archive означает, что статическая библиотека линкуется как целый архив без выбрасывания каких-либо объектных файлов.

Значение по умолчанию для этого модификатора - -whole-archive.

Более подробная информация о реализации этого модификатора может быть найдена в документации whole-archive для rustc.

Модификаторы линковки: verbatim

Этот модификатор совместим со всеми типами линковки.

+verbatim означает, что сам rustc не будет добавлять какие-либо целевые префиксы или суффиксы библиотек (как lib или .a) к имени библиотеки и постарается запросить то же самое от линковщика.

-verbatim означает, что rustc либо добавит целеспецифичный префикс и суффикс к имени библиотеки перед передачей его линковщику, либо не предотвратит неявное добавление его линковщиком.

Значение по умолчанию для этого модификатора - -verbatim.

Более подробная информация о реализации этого модификатора может быть найдена в документации verbatim для rustc.

dylib versus raw-dylib

На Windows линковка против динамической библиотеки требует, чтобы библиотека импорта была предоставлена линковщику: это специальная статическая библиотека, которая объявляет все символы, экспортируемые динамической библиотекой, таким образом, что линковщик знает, что они должны быть динамически загружены во время выполнения.

Указание kind = "dylib" инструктирует компилятор Rust линковать библиотеку импорта на основе ключа name. Линковщик затем использует свою обычную логику разрешения библиотек, чтобы найти эту библиотеку импорта. Альтернативно, указание kind = "raw-dylib" инструктирует компилятор сгенерировать библиотеку импорта во время компиляции и предоставить её линковщику вместо этого.

raw-dylib поддерживается только на Windows. Использование его при нацеливании на другие платформы приведёт к ошибке компилятора.

Ключ import_name_type

На x86 Windows имена функций “декорируются” (т.е., к ним добавляется определённый префикс и/или суффикс), чтобы указать их соглашение о вызовах. Например, функция с соглашением о вызовах stdcall с именем fn1, которая не имеет аргументов, будет декорирована как _fn1@0. Однако PE Format также разрешает именам не иметь префикса или быть недекорированными. Дополнительно, инструментарии MSVC и GNU используют разные декорации для одних и тех же соглашений о вызовах, что означает, по умолчанию некоторые функции Win32 не могут быть вызваны с использованием типа линковки raw-dylib через инструментарий GNU.

Чтобы разрешить эти различия, при использовании типа линковки raw-dylib вы можете также указать ключ import_name_type с одним из следующих значений, чтобы изменить то, как функции именуются в сгенерированной библиотеке импорта:

  • decorated: Имя функции будет полностью декорировано с использованием формата инструментария MSVC.
  • noprefix: Имя функции будет декорировано с использованием формата инструментария MSVC, но пропуская ведущий ?, @ или опционально _.
  • undecorated: Имя функции не будет декорировано.

Если ключ import_name_type не указан, то имя функции будет полностью декорировано с использованием формата целевого инструментария.

Переменные никогда не декорируются, поэтому ключ import_name_type не влияет на то, как они именуются в сгенерированной библиотеке импорта.

Ключ import_name_type поддерживается только на x86 Windows. Использование его при нацеливании на другие платформы приведёт к ошибке компилятора.

Атрибут link_name атрибут может быть применён к объявлениям внутри блока extern для указания символа для импорта для данной функции или статической переменной.

Example

#![allow(unused)]
fn main() {
unsafe extern "C" {
    #[link_name = "actual_symbol_name"]
    safe fn name_in_rust();
}
}

Атрибут link_name использует синтаксис MetaNameValueStr.

Атрибут link_name может быть применён только к функции или статическому элементу во внешнем блоке.

Note

rustc игнорирует использование в других позициях, но выдает предупреждение. Это может стать ошибкой в будущем.

Только последнее использование link_name на элементе имеет эффект.

Note

rustc выдает предупреждение на любое использование, предшествующее последнему. Это может стать ошибкой в будущем.

Атрибут link_name не может использоваться с атрибутом link_ordinal.

Атрибут link_ordinal может быть применён к объявлениям внутри внешнего блока для указания числового ординала, который следует использовать при генерации библиотеки импорта для линковки. Ординал - это уникальное число для каждого символа, экспортируемого динамической библиотекой на Windows, и может использоваться при загрузке библиотеки для нахождения этого символа вместо необходимости искать его по имени.

Warning

link_ordinal должен использоваться только в случаях, когда ординал символа известен как стабильный: если ординал символа не установлен явно при сборке содержащего его бинарника, то ему будет автоматически назначен ординал, и этот назначенный ординал может изменяться между сборками бинарника.

#![allow(unused)]
fn main() {
#[cfg(all(windows, target_arch = "x86"))]
#[link(name = "exporter", kind = "raw-dylib")]
unsafe extern "stdcall" {
    #[link_ordinal(15)]
    safe fn imported_function_stdcall(i: i32);
}
}

Этот атрибут используется только с типом линковки raw-dylib. Использование любого другого типа приведёт к ошибке компилятора.

Использование этого атрибута с атрибутом link_name приведёт к ошибке компилятора.

Атрибуты на параметрах функций

Атрибуты на параметрах внешних функций следуют тем же правилам и ограничениям, что и обычные параметры функций.


  1. Начиная с редакции 2024, ключевое слово unsafe требуется семантически.