Внешние блоки
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 "cdecl"— Соглашение о вызовах, обычно используемое с кодом C на x86_32.- Доступно только для целей x86_32.
- Соответствует
__cdeclв MSVC и__attribute__((cdecl))в GCC и clang.
Note
Для подробностей см.:
-
unsafe extern "stdcall"— Соглашение о вызовах, обычно используемое Win32 API на x86_32.- Доступно только для целей x86_32.
- Соответствует
__stdcallв MSVC и__attribute__((stdcall))в GCC и clang.
Note
Для подробностей см.:
-
unsafe extern "win64"— ABI Windows x64.- Доступно только для целей x86_64.
- “win64” такой же, как ABI “C” на целях Windows x86_64.
- Соответствует
__attribute__((ms_abi))в GCC и clang.
-
unsafe extern "sysv64"— ABI System V.- Доступно только для целей x86_64.
- “sysv64” такой же, как ABI “C” на целях не-Windows x86_64.
- Соответствует
__attribute__((sysv_abi))в GCC и clang.
Note
Для подробностей см.:
-
unsafe extern "aapcs"— ABI с программной эмуляцией чисел с плавающей точкой для ARM.- Доступно только для целей ARM32.
- “aapcs” такой же, как ABI “C” на ARM32 с программной эмуляцией чисел с плавающей точкой.
- Соответствует
__attribute__((pcs("aapcs")))в clang.
Note
Для подробностей см.:
-
unsafe extern "fastcall"— “Быстрая” вариант stdcall, который передаёт некоторые аргументы в регистрах.- Доступно только для целей x86_32.
- Соответствует
__fastcallв MSVC и__attribute__((fastcall))в GCC и clang.
-
unsafe extern "thiscall"— Соглашение о вызовах, обычно используемое для функций-членов классов C++ на x86_32 MSVC.- Доступно только для целей x86_32.
- Соответствует
__thiscallв MSVC и__attribute__((thiscall))в GCC и 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
Атрибут link указывает имя нативной библиотеки, с которой
компилятор должен линковаться для элементов внутри блока extern.
Он использует синтаксис MetaListNameValueStr для указания своих входных данных. Ключ name - это
имя нативной библиотеки для линковки. Ключ kind - это опциональное значение, которое
указывает тип библиотеки со следующими возможными значениями:
dylib— Указывает динамическую библиотеку. Это значение по умолчанию, еслиkindне указан.
static— Указывает статическую библиотеку.
framework— Указывает фреймворк macOS. Это действительно только для целей macOS.
raw-dylib— Указывает динамическую библиотеку, для которой компилятор сгенерирует библиотеку импорта для линковки (см.dylibversusraw-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
Атрибут 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
Атрибут 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 приведёт к
ошибке компилятора.
Атрибуты на параметрах функций
Атрибуты на параметрах внешних функций следуют тем же правилам и ограничениям, что и обычные параметры функций.
-
Начиная с редакции 2024, ключевое слово
unsafeтребуется семантически. ↩