函数

语法

函数 :

   函数限定符 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 版本中,只有类型的函数参数只允许在 特征项 的关联函数中使用。

函数 由一个 代码块、一个名称、一组参数和一个输出类型组成。除了名称之外,所有这些都是可选的。函数使用关键字 fn 声明。函数可以声明一组输入 变量 作为参数,调用者通过这些参数将参数传递给函数,以及函数在完成后将返回给调用者的值的输出 类型。如果没有显式声明输出类型,则为 单元类型

当被引用时,函数 会产生一个对应于零大小 函数项类型 的一等,该值在被调用时会直接调用该函数。

例如,这是一个简单的函数

#![allow(unused)]
fn main() {
fn answer_to_life_the_universe_and_everything() -> i32 {
    return 42;
}
}

函数参数

函数参数是不可反驳的 模式,因此任何在无 else 的 let 绑定中有效的模式也可用作参数

#![allow(unused)]
fn main() {
fn first((value, _): (i32, i32)) -> i32 { value }
}

如果第一个参数是 Self参数,则表示该函数是一个 方法。具有 self 参数的函数只能作为 特征实现 中的 关联函数 出现。

带有 ... 词法单元的参数表示一个 可变参数函数,并且只能用作 外部块 函数的最后一个参数。可变参数可以有一个可选的标识符,例如 args: ...

函数体

函数的代码块在概念上被包装在一个代码块中,该代码块绑定参数模式,然后 return 函数代码块的值。这意味着代码块的尾表达式(如果被求值)最终会被返回给调用者。像往常一样,如果到达函数体内显式返回表达式,则会 shortcut 该隐式返回。

例如,上面的函数的行为就像它被写成这样

// argument_0 is the actual first argument passed from the caller
let (value, _) = argument_0;
return {
    value
};

没有代码块体的函数以分号结尾。这种形式只能出现在 特征外部块 中。

泛型函数

泛型函数 允许在其签名中出现一个或多个参数化类型。每个类型参数都必须在函数名后面的尖括号括起来的逗号分隔列表中显式声明。

#![allow(unused)]
fn main() {
// foo is generic over A and 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 {
    // details elided
}

foo(&[1, 2]);
}

将使用 i32 实例化类型参数 T

类型参数也可以在函数名后面的尾随 路径 组件中显式提供。如果没有足够的上下文来确定类型参数,这可能是必要的。例如,mem::size_of::<u32>() == 4

外部函数限定符

extern 函数限定符允许提供可以使用特定 ABI 调用的函数定义

extern "ABI" fn foo() { /* ... */ }

这些通常与提供函数声明外部块项结合使用,这些函数声明可用于调用函数而无需提供其定义

extern "ABI" {
  fn foo(); /* no body */
}
unsafe { foo() }

当函数项中的 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(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;
}

ABI 与 "Rust" 不同的函数不支持与 Rust 完全相同的方式展开。因此,展开超过具有此类 ABI 的函数的末尾会导致进程中止。

注意rustc 实现的 LLVM 后端通过执行非法指令来中止进程。

常量函数

使用 const 关键字限定的函数是常量函数元组结构体元组变体构造函数也是如此。常量函数可以从常量上下文中调用。

常量函数可以使用extern函数限定符,但只能使用 "Rust""C" ABI。

常量函数不允许是异步的

异步函数

函数可以限定为异步的,这也可以与 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 的生命周期,因此捕获了它。
  • 主体中的async move捕获所有函数参数,包括未使用的或绑定到 _ 模式的参数。这确保函数参数按照与非异步函数相同的顺序被丢弃,只是丢弃发生在返回的 future 被完全等待之后。

有关异步效果的更多信息,请参阅async

版本差异:异步函数仅从 Rust 2018 开始可用。

组合 asyncunsafe

声明一个既是异步又是 unsafe 的函数是合法的。生成的函数调用起来不安全,并且(像任何异步函数一样)返回一个 future。这个 future 只是一个普通的 future,因此不需要 unsafe 上下文来“等待”它

#![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 作为参数,然后(在等待时)解引用该指针。这意味着 x 必须在 future 完成执行之前一直有效,并且确保这一点是调用者的责任。

函数上的属性

外部属性允许在函数上使用。内部属性允许直接在其内的 { 之后使用。

此示例显示了函数上的内部属性。该函数仅使用“示例”一词记录。

#![allow(unused)]
fn main() {
fn documented() {
    #![doc = "Example"]
}
}

注意:除了 lint 之外,习惯上只在函数项上使用外部属性。

对函数有意义的属性是cfgcfg_attrdeprecateddocexport_namelink_sectionno_manglelint 检查属性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) {
}