函数
语法
函数 :
函数修饰符组fn
标识符 泛型参数组?
(
函数参数组?)
函数返回类型? Where从句?
( 块表达式 |;
)函数修饰符组 :
const
?async
1?unsafe
? (extern
Abi?)?函数参数组 :
Self参数,
?
| (Self参数,
)? 函数参数 (,
函数参数)*,
?Self参数 :
外围属性* ( 简写Self | 类型化Self )简写Self :
(&
|&
生命周期)?mut
?self
类型化Self :
mut
?self
:
类型函数参数 :
外围属性* ( 函数参数模式 |...
| 模式 2 )函数参数模式 :
模式非顶层项:
( 类型 |...
)函数返回类型 :
->
类型1在 2015 版中不允许使用
async
限定。2在 2015 版中,只有在一个 trait 条目 的关联函数中才允许使用仅带有类型的函数参数。
函数包括一个 块 ,以及一个名称、一组参数和一个输出类型。
除名称外,其他是可选的。函数用关键字 fn
声明。
函数可以声明一组 输入 变量 作为参数,通过这些变量,调用者将参数传递给函数,并且函数的 输出 类型为函数完成时将返回给其调用者的 类型 。
如果未显式指定输出类型,则为 单元类型 。
当引用一个函数时,会产生一个大小为零的 函数条目类型 第一类 值 ,当通过引用调用时会直接调用该函数。
例如,以下是一个简单的函数:
#![allow(unused)] fn main() { fn answer_to_life_the_universe_and_everything() -> i32 { return 42; } }
函数参数
函数参数是不可拒绝的 模式 ,对于在无其他附加的 let
绑定中有效的模式在参数中也是有效的:
#![allow(unused)] fn main() { fn first((value, _): (i32, i32)) -> i32 { value } }
如果第一个参数是 Self参数 ,则表示该函数是一个 方法 。
带有 self
参数的函数只能作为 trait 或 实现 中的关联函数出现。
具有 ...
符号的参数表示是一个 可变参数函数 ,且只能作为 外部块函数 的最后一个参数。
可变参数可以有一个可选的标识符,例如 args: ...
。
函数体
函数的代码块在概念上被包装在一个块中,该块绑定了参数模式,然后 return
函数块的值。
这意味着,如果块的最终表达式被求解,将返回给调用者。
和常规一样,如果在函数体内有显式的返回表达式,则会立即返回,如果到达该隐式返回,则会被短路。
例如,上面的函数的行为类似于以下写法:
// argument_0 是调用者实际传递的第一个参数。
let (value, _) = argument_0;
return {
value
};
函数没有函数体时以分号结束。这种形式只能出现在 trait 或 外部块 中。
泛型函数
泛型函数 允许其签名中出现一个或多个 参数化类型 。 每个类型参数必须在一个尖括号包围的、由逗号分隔的列表中显式声明,在函数名之后。
#![allow(unused)] fn main() { // foo 中 A 和 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]); }
将类型参数 T
实例化为 i32
。
函数的类型参数还可以在函数名后跟一个后缀 路径 组件来明确指定。
如果没有足够的上下文来确定类型参数,则可能需要这样做。例如,mem::size_of::<u32>() == 4
。
外部函数修饰
extern
函数修饰符允许提供函数 定义组 ,这些函数可以使用特定的 ABI 进行调用:
extern "ABI" fn foo() { /* ... */ }
这些通常与 外部块 条目结合使用,可以提供用来调用函数的声明,而不必提供其函数 定义 :
extern "ABI" {
fn foo(); /* no body */
}
unsafe { foo() }
当在函数条目中省略 "extern" Abi?*
时,ABI 被分配 "Rust"
。例如:
#![allow(unused)] fn main() { fn foo() {} }
等价于:
#![allow(unused)] fn main() { extern "Rust" fn foo() {} }
ABI 使用非 "Rust" 的函数,允许其他编程语言调用该函数,比如 C 语言:
#![allow(unused)] fn main() { // 用 "C" ABI 声明函数 extern "C" fn new_i32() -> i32 { 0 } // 用 "stdcall" ABI 声明函数 #[cfg(target_arch = "x86_64")] 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; }
非 "Rust" 函数的 ABI (应用程序二进制接口) 不能像 Rust 一样支持栈回退,因此尝试在这些 ABI 中回退超过函数末尾的部分会导致进程中止。
注意:
rustc
的 LLVM 后端通过执行非法指令来使进程中止。
Const 函数
被 const
修饰的函数是 常量函数 ,同样的构造函数 元组结构体 和 元组变体 也是。
常量函数 可以在 常量上下文 中被调用。
const
函数可以使用 extern
函数限定符,但是只能使用 "Rust"
和 "C"
ABI。
Const 函数不允许是 async 。
异步函数
函数可以被标记为 async "异步" 的,这也可以与 unsafe
关键字组合使用。
#![allow(unused)] fn main() { async fn regular_example() { } async unsafe fn unsafe_example() { } }
异步函数在调用时,不会立即执行,而是将其参数捕获为一个 "future" 。当该 future 被轮询时,会执行函数体。
一个 async 异步函数大致等同于一个返回 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; // 去除语法糖 fn example<'a>(x: &'a str) -> impl Future<Output = usize> + 'a { async move { x.len() } } }
实际的脱糖更加复杂:
async fn
声明中的所有生命周期参数会被假定为被返回类型所包含。这在上面的展开示例中可以看到,显式地包含了'a
生命周期。- 函数体中的
async move
块 捕获了所有函数参数,包括那些未使用或绑定到_
模式的参数。 这确保了函数参数以与非异步函数相同的顺序被释放,除了释放操作在完全等待返回的 future 后发生。
有关 async
的更多信息,请参见 async
块。
版本差异: 异步函数仅在 Rust 2018 开始提供。
组合 async
和 unsafe
在 Rust 中,声明既是 async
又是 unsafe
的函数是合法的。
这样产生的函数是非安全的,不能安全地调用,但它 (像任何异步函数一样) 返回一个 future。
这个 future 是普通的 future,因此在 "await" 它的时候不需要使用 unsafe
上下文。
#![allow(unused)] fn main() { // 返回一个 future ,当被等待时,解除对 `x` 的引用。 // // 必要条件: `x` 必须是安全的,在产生的 future 完成之前,可以取消引用。 async unsafe fn unsafe_example(x: *const i32) -> i32 { *x } async fn safe_example() { // 初始需要一个 `unsafe` 块来调用这个函数: let p = 22; let future = unsafe { unsafe_example(&p) }; // 但这里不需要 `unsafe` 块。这将读取 `p` 的值: let q = future.await; } }
注意,这个行为是由将函数展开为返回 impl Future
的行为所决定的。
在这个例子中,我们展开的函数是一个 unsafe
函数,但是返回值仍然是一样的。
unsafe
关键字在异步函数中的使用方式与其在其他函数中的使用方式相同:它表示该函数对其调用者有一些额外的义务,以确保其安全性。
与任何其他非安全函数一样,这些条件可能会超出初始调用本身的范围。
例如,在上面的代码段中, unsafe_example
函数接受一个指针 x
作为参数,然后 (当等待时) 对该指针进行解引用。
这意味着 x
必须在 future 执行完成之前保持有效,并且调用者有责任确保这一点。
函数的属性
函数可以使用 外围属性 。 内部属性 可以直接在函数的 块 里的 {
后面使用。
以下示例展示了在函数中使用内部属性。该函数的文档注释只包含单词 "Example" 。
#![allow(unused)] fn main() { fn documented() { #![doc = "Example"] } }
注意: 除了用于代码分析 ,通常仅在函数条目上使用外围属性是惯用的。
函数可用的属性包括 cfg
、 cfg_attr
、 deprecated
、 doc
、 export_name
、 link_section
、 no_mangle
、代码分析 、 must_use
、 过程宏属性 、 测试 和 优化提示 。此外,函数还接受属性宏。
函数参数的属性
函数参数上允许使用 外围属性 ,其中允许使用的 内置属性 仅限于 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) {
}