函数
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 块中使用。
函数参数
函数参数是不可反驳的模式,因此在没有 else 的 let 绑定中有效的任何模式作为参数也有效:
#![allow(unused)]
fn main() {
fn first((value, _): (i32, i32)) -> i32 { value }
}
具有 self 参数的函数只能作为 trait 或实现中的关联函数出现。
带有 ... 词法单元的参数表示可变参数函数,只能用作外部块函数的最后一个参数。可变参数可以有一个可选的标识符,例如 args: ...。
函数主体
函数的主体块在概念上被包装在另一个块中,该块首先绑定参数模式,然后 return 函数主体的值。这意味着,如果计算了块的尾部表达式,它最终将返回给调用者。像往常一样,函数主体内的显式 return 表达式如果到达,将短路该隐式返回。
例如,上面的函数的行为就像它是这样编写的:
// argument_0 is the actual first argument passed from the caller
let (value, _) = argument_0;
return {
value
};
没有主体块的函数以分号终止。此形式只能出现在 trait 或外部块中。
泛型函数
泛型函数_允许其签名中出现一个或多个_参数化类型。每个类型参数必须在函数名后面的尖括号括起来的逗号分隔列表中显式声明。
#![allow(unused)]
fn main() {
// foo is generic over A and B
fn foo<A, B>(x: A, y: B) {
}
}
在函数签名和主体内部,类型参数的名称可以用作类型名称。
可以为类型参数指定 Trait 约束,以允许在该类型的值上调用该 trait 的方法。这是使用 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 {
// details elided
}
foo(&[1, 2]);
}
将使用 i32 实例化类型参数 T。
类型参数也可以在函数名后面的尾部路径组件中显式提供。如果没有足够的上下文来确定类型参数,这可能是必要的。例如,mem::size_of::<u32>() == 4。
Extern 函数限定符
extern 函数限定符允许提供可以使用特定 ABI 调用的函数_定义_:
extern "ABI" fn foo() { /* ... */ }
这些通常与外部块项结合使用,后者提供函数_声明_,可用于调用函数而无需提供其_定义_:
unsafe extern "ABI" {
unsafe fn foo(); /* no body */
safe fn bar(); /* no body */
}
unsafe { foo() };
bar();
当函数项中的 FunctionQualifiers 省略 "extern" Abi?* 时,将分配 ABI "Rust"。例如:
#![allow(unused)]
fn main() {
fn foo() {}
}
等同于:
#![allow(unused)]
fn main() {
extern "Rust" fn foo() {}
}
函数可以被外部代码调用,使用与 Rust 不同的 ABI 允许,例如,提供可以从 C 等其他编程语言调用的函数:
#![allow(unused)]
fn main() {
// Declares a function with the "C" ABI
extern "C" fn new_i32() -> i32 { 0 }
// Declares a function with the "stdcall" ABI
#[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 后缀,一种不带。Rust ABI 始终允许展开,因此没有 Rust-unwind ABI。ABI 的选择以及运行时 panic 处理器决定了展开函数时的行为。
下表表示展开操作到达每种类型 ABI 边界(使用相应 ABI 字符串的函数声明或定义)时的行为。请注意,Rust 运行时不受任何完全在另一种语言的运行时内发生的展开的影响,也不能对其产生影响,即那些被抛出和捕获而未到达 Rust ABI 边界的展开。
panic-unwind 列指的是通过 panic! 宏和类似标准库机制进行的 panicking,以及导致 panic 的任何其他 Rust 操作,例如越界数组索引或整数溢出。
“unwinding” ABI 类别指的是 "Rust"(未标记 extern 的 Rust 函数的隐式 ABI)、"C-unwind" 以及名称中带有 -unwind 的任何其他 ABI。“non-unwinding” ABI 类别指的是所有其他 ABI 字符串,包括 "C" 和 "stdcall"。
本机展开是按目标定义的。在支持抛出和捕获 C++ 异常的平台上,它指的是用于实现此功能的某些平台实现了一种称为“强制展开”的展开形式;Windows 上的 longjmp 和 glibc 中的 pthread_exit 就是这样实现的。强制展开被明确排除在表中的“本机展开“列之外。
| panic 运行时 | ABI | panic-unwind | 本机展开(非强制) |
|---|---|---|---|
panic=unwind | unwinding | 展开 | 展开 |
panic=unwind | non-unwinding | 中止(见下文注释) | [未定义行为] |
panic=abort | unwinding | panic 中止而不展开 | 中止 |
panic=abort | non-unwinding | panic 中止而不展开 | [未定义行为] |
使用 panic=unwind 时,当 panic 被 non-unwinding ABI 边界转换为中止时,要么不运行任何析构器(Drop 调用),要么运行直到 ABI 边界的所有析构器。未指定这两种行为中的哪一种会发生。
有关跨 FFI 边界展开的其他注意事项和限制,请参阅 Panic 文档中的相关部分。
Const 函数
有关 const 函数的定义,请参阅 const 函数。
异步函数
函数可以被限定为 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() {
// Source
async fn example(x: &str) -> usize {
x.len()
}
}
大致等同于:
#![allow(unused)]
fn main() {
use std::future::Future;
// Desugared
fn example<'a>(x: &'a str) -> impl Future<Output = usize> + 'a {
async move { x.len() }
}
}
实际的脱糖更复杂:
- 脱糖中的返回类型被假定为捕获
async fn声明中的所有生命周期参数。这可以在上面的脱糖示例中看到,它显式地比'a活得更久,因此捕获了'a。
- 主体中的
async move块捕获所有函数参数,包括那些未使用或绑定到_模式的参数。这确保函数参数以与函数不是 async 时相同的顺序被丢弃,只是丢弃发生在返回的 future 被完全 await 时。
有关 async 效果的更多信息,请参阅 async 块。
2018 Edition differences
异步函数仅从 Rust 2018 版本开始可用。
组合 async 和 unsafe
声明一个既是 async 又是 unsafe 的函数是合法的。生成的函数调用是不安全的,并且(像任何异步函数一样)返回一个 future。这个 future 只是一个普通的 future,因此不需要 unsafe 上下文来“await“它:
#![allow(unused)]
fn main() {
// Returns a future that, when awaited, dereferences `x`.
//
// Soundness condition: `x` must be safe to dereference until
// the resulting future is complete.
async unsafe fn unsafe_example(x: *const i32) -> i32 {
*x
}
async fn safe_example() {
// An `unsafe` block is required to invoke the function initially:
let p = 22;
let future = unsafe { unsafe_example(&p) };
// But no `unsafe` block required here. This will
// read the value of `p`:
let q = future.await;
}
}
请注意,此行为是脱糖为返回 impl Future 的函数的结果 – 在这种情况下,我们脱糖到的函数是一个 unsafe 函数,但返回值保持不变。
Unsafe 在异步函数上的使用方式与其他函数完全相同:它表示该函数对其调用者施加了一些额外的义务以确保健全性。与任何其他 unsafe 函数一样,这些条件可能超出初始调用本身 – 例如,在上面的代码片段中,unsafe_example 函数接受指针 x 作为参数,然后(当 await 时)解引用该指针。这意味着 x 必须在 future 完成执行之前有效,调用者有责任确保这一点。
函数上的属性
函数上允许使用外部属性。在其主体块内的 { 之后直接允许使用内部属性。
此示例显示了函数上的内部属性。该函数仅用“Example“一词记录。
#![allow(unused)]
fn main() {
fn documented() {
#![doc = "Example"]
}
}
Note
除了 lint 之外,习惯上只在函数项上使用外部属性。
在函数上有意义的属性是:
cfg_attrcfgcolddeprecateddocexport_nameinlinelink_sectionmust_useno_mangle- [Lint 检查属性]
- [过程宏属性]
- [测试属性]
函数参数上的属性
函数参数上允许使用外部属性,允许的内置属性仅限于 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) {
}