Функции
Syntax
Function →
FunctionQualifiers fn IDENTIFIER GenericParams?
( FunctionParameters? )
FunctionReturnType? WhereClause?
( BlockExpression | ; )
FunctionQualifiers → const? async?1 ItemSafety?2 ( extern Abi? )?
ItemSafety → safe3 | unsafe
Abi → STRING_LITERAL | RAW_STRING_LITERAL
FunctionParameters →
SelfParam ,?
| ( SelfParam , )? FunctionParam ( , FunctionParam )* ,?
SelfParam → OuterAttribute* ( ShorthandSelf | TypedSelf )
ShorthandSelf → ( & | & Lifetime )? mut? self
FunctionParam → OuterAttribute* ( FunctionParamPattern | ... | Type4 )
FunctionParamPattern → PatternNoTopAlt : ( Type | ... )
FunctionReturnType → -> Type
Функция состоит из блока (это тело функции), наряду с именем, набором параметров и типом возвращаемого значения. Кроме имени, все эти элементы являются опциональными.
Функции объявляются с ключевым словом fn, которое определяет данное имя в пространстве имён значений модуля или блока, где она находится.
Функции могут объявлять набор входных переменных в качестве параметров, через которые вызывающая сторона передаёт аргументы в функцию, и выходной тип значения, которое функция вернёт своей вызывающей стороне при завершении.
Если тип возвращаемого значения не указан явно, это единичный тип.
При ссылке на функцию, она порождает первоклассное значение соответствующего нулевого размера типа элемента функции, которое при вызове вычисляется в прямой вызов функции.
Например, это простая функция:
#![allow(unused)] fn main() { fn answer_to_life_the_universe_and_everything() -> i32 { return 42; } }
Квалификатор safe для функции семантически разрешён только при использовании в блоке extern.
Параметры функций
Параметры функций являются неопровержимыми образцами, поэтому любой образец, который допустим в
привязке let без else, также допустим в качестве параметра:
#![allow(unused)] fn main() { fn first((value, _): (i32, i32)) -> i32 { value } }
Если первый параметр является SelfParam, это указывает, что функция является методом.
Функции с параметром self могут появляться только как ассоциированная
функция в трейте или реализации.
Параметр с токеном ... указывает на вариадическую функцию и может
использоваться только как последний параметр функции внешнего блока. Вариадический
параметр может иметь опциональный идентификатор, такой как args: ....
Тело функции
Блочное тело функции концептуально обёрнуто в другой блок, который сначала связывает образцы
аргументов, а затем return возвращает значение тела функции. Это
означает, что конечное выражение блока, если оно вычисляется, в конечном итоге
возвращается вызывающей стороне. Как обычно, явное выражение возврата внутри
тела функции сократит этот неявный возврат, если будет достигнуто.
Например, функция выше ведёт себя так, как если бы она была написана как:
// argument_0 - это фактический первый аргумент, переданный от вызывающей стороны
let (value, _) = argument_0;
return {
value
};
Функции без блока тела завершаются точкой с запятой. Эта форма может появляться только в трейте или внешнем блоке.
Обобщённые функции
Обобщённая функция позволяет одному или нескольким параметризованным типам появляться в её сигнатуре. Каждый параметр типа должен быть явно объявлен в списке, разделённом запятыми и заключённом в угловые скобки, после имени функции.
#![allow(unused)] fn main() { // foo обобщена по A и B fn foo<A, B>(x: A, y: B) { } }
Внутри сигнатуры и тела функции имя параметра типа может быть использовано как имя типа.
Ограничения трейтов могут быть указаны для параметров
типа, чтобы позволить вызывать методы этого трейта для значений этого
типа. Это указывается с использованием синтаксиса where:
#![allow(unused)] fn main() { use std::fmt::Debug; fn foo<T>(x: T) where T: Debug { } }
Когда на обобщённую функцию ссылаются, её тип инстанцируется на основе
контекста ссылки. Например, вызов функции foo здесь:
#![allow(unused)] fn main() { use std::fmt::Debug; fn foo<T>(x: &[T]) where T: Debug { // детали опущены } foo(&[1, 2]); }
инстанцирует параметр типа T как i32.
Параметры типа также могут быть явно указаны в завершающем компоненте пути
после имени функции. Это может быть необходимо, если нет
достаточного контекста для определения параметров типа. Например,
mem::size_of::<u32>() == 4.
Квалификатор внешней функции
Квалификатор extern позволяет предоставлять определения функций, которые могут
быть вызваны с определённым ABI:
extern "ABI" fn foo() { /* ... */ }
Они часто используются в сочетании с элементами внешнего блока, которые предоставляют объявления функций, которые можно использовать для вызова функций без предоставления их определения:
unsafe extern "ABI" {
unsafe fn foo(); /* нет тела */
safe fn bar(); /* нет тела */
}
unsafe { foo() };
bar();
Когда "extern" Abi?* опущен из FunctionQualifiers в элементах функции,
присваивается ABI "Rust". Например:
#![allow(unused)] fn main() { fn foo() {} }
эквивалентно:
#![allow(unused)] fn main() { extern "Rust" fn foo() {} }
Функции могут вызываться иностранным кодом, и использование ABI, которое отличается от Rust, позволяет, например, предоставлять функции, которые могут быть вызваны из других языков программирования, таких как C:
#![allow(unused)] fn main() { // Объявляет функцию с ABI "C" extern "C" fn new_i32() -> i32 { 0 } // Объявляет функцию с ABI "stdcall" #[cfg(any(windows, target_arch = "x86"))] extern "stdcall" fn new_i32_stdcall() -> i32 { 0 } }
Так же, как с внешним блоком, когда используется ключевое слово extern и "ABI"
опущен, используемый ABI по умолчанию становится "C". То есть это:
#![allow(unused)] fn main() { extern fn new_i32() -> i32 { 0 } let fptr: extern fn() -> i32 = new_i32; }
эквивалентно:
#![allow(unused)] fn main() { extern "C" fn new_i32() -> i32 { 0 } let fptr: extern "C" fn() -> i32 = new_i32; }
Раскрутка стека
Большинство строк ABI имеют два варианта: один с суффиксом -unwind и один без. ABI Rust всегда разрешает раскрутку стека, поэтому нет ABI Rust-unwind. Выбор ABI вместе с обработчиком паники времени выполнения определяет поведение при раскрутке стека из функции.
В таблице ниже показано поведение операции раскрутки стека, достигающей каждого типа границы ABI (объявление или определение функции с использованием соответствующей строки ABI). Обратите внимание, что среда выполнения Rust не затрагивается и не может влиять на любую раскрутку стека, которая происходит полностью внутри среды выполнения другого языка, то есть раскрутки стека, которые выбрасываются и перехватываются без достижения границы ABI Rust.
Столбец panic-unwind относится к панике через макрос panic! и аналогичные механизмы стандартной библиотеки, а также к любым другим операциям Rust, которые вызывают панику, таким как индексация массива вне границ или переполнение целого числа.
Категория ABI “unwinding” относится к "Rust" (неявный ABI функций Rust, не помеченных extern), "C-unwind" и любым другим ABI с -unwind в их имени. Категория ABI “non-unwinding” относится ко всем другим строкам ABI, включая "C" и "stdcall".
Нативная раскрутка стека определяется для каждой цели. На целях, которые поддерживают выбрасывание и перехват исключений C++, это относится к механизму, используемому для реализации этой функции. Некоторые платформы реализуют форму раскрутки стека, называемую “принудительной раскруткой стека”; longjmp на Windows и pthread_exit в glibc реализованы таким образом. Принудительная раскрутка стека явно исключена из столбца “Native unwind” в таблице.
| Среда выполнения паники | ABI | panic-unwind | Нативная раскрутка стека (непринудительная) |
|---|---|---|---|
panic=unwind | unwinding | раскрутка стека | раскрутка стека |
panic=unwind | non-unwinding | прерывание (см. примечания ниже) | неопределённое поведение |
panic=abort | unwinding | panic прерывает без раскрутки стека | прерывание |
panic=abort | non-unwinding | panic прерывает без раскрутки стека | неопределённое поведение |
С panic=unwind, когда panic превращается в прерывание границей ABI без раскрутки стека, либо не будут запущены никакие деструкторы (вызовы Drop), либо все деструкторы до границы ABI будут запущены. Не указано, какое из этих двух поведений произойдёт.
Для других соображений и ограничений относительно раскрутки стека через границы FFI см. соответствующий раздел в документации по Panic.
Константные функции
См. константные функции для определения константных функций.
Асинхронные функции
Функции могут быть квалифицированы как async, и это также может быть объединено с
квалификатором unsafe:
#![allow(unused)] fn main() { async fn regular_example() { } async unsafe fn unsafe_example() { } }
Асинхронные функции не выполняют работу при вызове: вместо этого они захватывают свои аргументы в future. При опросе этот future будет выполнять тело функции.
Асинхронная функция примерно эквивалентна функции,
которая возвращает impl Future и с async move блоком в качестве
своего тела:
#![allow(unused)] fn main() { // Исходный код async fn example(x: &str) -> usize { x.len() } }
грубо эквивалентно:
#![allow(unused)] fn main() { use std::future::Future; // Десугаризованный fn example<'a>(x: &'a str) -> impl Future<Output = usize> + 'a { async move { x.len() } } }
Фактическая десугаризация более сложна:
- Предполагается, что тип возвращаемого значения в десугаризации захватывает все параметры
времени жизни из объявления
async fn. Это можно увидеть в десугаризованном примере выше, который явно переживает и, следовательно, захватывает'a.
async moveблок в теле захватывает все параметры функции, включая те, которые не используются или привязаны к образцу_. Это гарантирует, что параметры функции удаляются в том же порядке, как если бы функция не была асинхронной, за исключением того, что удаление происходит, когда возвращённый future был полностью ожидан.
Для получения дополнительной информации о эффекте async см. async блоки.
2018 Edition differences
Асинхронные функции доступны только начиная с Rust 2018.
Комбинирование async и unsafe
Законно объявлять функцию, которая является одновременно async и unsafe. Результирующая функция небезопасна для вызова и (как и любая асинхронная функция) возвращает future. Этот future - просто обычный future, и поэтому небезопасный контекст не требуется для его “ожидания”:
#![allow(unused)] fn main() { // Возвращает future, который при ожидании разыменовывает `x`. // // Условие звуковости: `x` должен быть безопасен для разыменования до // завершения результирующего future. async unsafe fn unsafe_example(x: *const i32) -> i32 { *x } async fn safe_example() { // Небезопасный блок требуется для первоначального вызова функции: let p = 22; let future = unsafe { unsafe_example(&p) }; // Но здесь небезопасный блок не требуется. Это будет // читать значение `p`: let q = future.await; } }
Обратите внимание, что это поведение является следствием десугаризации в
функцию, которая возвращает impl Future - в этом случае функция,
в которую мы десугаризируем, является unsafe функцией, но возвращаемое значение остаётся
тем же.
Unsafe используется в асинхронной функции точно так же, как он
используется в других функциях: это указывает, что функция накладывает
некоторые дополнительные обязательства на свою вызывающую сторону для обеспечения звуковости. Как и в любой
другой небезопасной функции, эти условия могут распространяться за пределы самого первоначального
вызова - в приведённом выше фрагменте, например, функция unsafe_example
принимала указатель x в качестве аргумента, а затем (при ожидании)
разыменовывала этот указатель. Это подразумевает, что x должен был бы
быть действительным до завершения выполнения future, и это ответственность
вызывающей стороны обеспечить это.
Атрибуты на функциях
Внешние атрибуты разрешены на функциях. Внутренние
атрибуты разрешены непосредственно после { внутри её тела блока.
Этот пример показывает внутренний атрибут на функции. Функция документирована только словом “Example”.
#![allow(unused)] fn main() { fn documented() { #![doc = "Example"] } }
Note
За исключением линтов, идиоматично использовать только внешние атрибуты на элементах функций.
Атрибуты, которые имеют смысл на функции:
cfg_attrcfgcolddeprecateddocexport_nameinlinelink_sectionmust_useno_mangle- Атрибуты проверки линтов
- Атрибуты процедурных макросов
- Атрибуты тестирования
Атрибуты на параметрах функций
Внешние атрибуты разрешены на параметрах функций и
разрешённые встроенные атрибуты ограничены cfg, cfg_attr, allow,
warn, deny и forbid.
#![allow(unused)] fn main() { fn len( #[cfg(windows)] slice: &[u16], #[cfg(not(windows))] slice: &[u8], ) -> usize { slice.len() } }
Инертные вспомогательные атрибуты, используемые атрибутами процедурных макросов, применёнными к элементам, также
разрешены, но будьте осторожны, чтобы не включать эти инертные атрибуты в ваш окончательный TokenStream.
Например, следующий код определяет инертный атрибут some_inert_attribute, который
нигде формально не определён, и процедурный макрос some_proc_macro_attribute отвечает
за обнаружение его присутствия и удаление из выходного потока токенов.
#[some_proc_macro_attribute]
fn foo_oof(#[some_inert_attribute] arg: u8) {
}
-
Квалификатор
asyncне разрешён в редакции 2015. ↩ -
Актуально для редакций ранее Rust 2024: Внутри блоков
externквалификатор функцииsafeилиunsafeразрешён только когдаexternквалифицирован какunsafe. ↩ -
Квалификатор функции
safeсемантически разрешён только внутри блоковextern. ↩ -
Параметры функций только с типом разрешены только в ассоциированной функции элемента трейта в редакции 2015. ↩