函数

语法
函数 :
   函数修饰符组 fn 标识符 泛型参数组?
      ( 函数参数组? )
      函数返回类型? Where从句?
      ( 块表达式 | ; )

函数修饰符组 :
   const? async1? unsafe? (extern Abi?)?

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 开始提供。

组合 asyncunsafe

在 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"]
}
}

注意: 除了用于代码分析 ,通常仅在函数条目上使用外围属性是惯用的。

函数可用的属性包括 cfgcfg_attrdeprecateddocexport_namelink_sectionno_mangle代码分析must_use过程宏属性测试优化提示 。此外,函数还接受属性宏。

函数参数的属性

函数参数上允许使用 外围属性 ,其中允许使用的 内置属性 仅限于 cfgcfg_attrallowwarndenyforbid

#![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) {
}