函数

语法
函数 :
   函数限定符 fn 标识符 泛型参数?
      ( 函数参数? )
      函数返回类型? Where 子句?
      ( 块表达式 | ; )

函数限定符 :
   const? async1? 项安全性? (extern Abi?)?

项安全性 :
   safe2 | unsafe

Abi :
   字符串字面量 | 原始字符串字面量

函数参数 :
      SelfParam ,?
   | (SelfParam ,)? 函数参数 (, 函数参数)* ,?

SelfParam :
   外部属性* ( 简写Self | 类型化Self )

简写Self :
   (& | & 生命周期)? mut? self

类型化Self :
   mut? self : 类型

函数参数 :
   外部属性* ( 函数参数模式 | ... | 类型 3 )

函数参数模式 :
   无顶层或模式 : ( 类型 | ... )

函数返回类型 :
   -> 类型

1

在 2015 版本中不允许使用 async 限定符。

2

safe 函数限定符在语义上仅允许在 extern 块中使用。

3

仅具有类型的函数参数仅在 2015 版本中的 trait 项的关联函数中允许。

一个函数由一个(即函数的主体),以及一个名称、一组参数和一个输出类型组成。除了名称之外,所有这些都是可选的。

函数使用关键字 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 }
}

如果第一个参数是 SelfParam,则表示该函数是方法

具有 self 参数的函数只能作为关联函数出现在trait实现中。

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

函数体

函数的 body 块在概念上被包裹在另一个块中,该块首先绑定参数模式,然后 return 函数 body 的值。这意味着如果评估了块的尾部表达式,则最终会返回给调用者。像往常一样,如果在函数主体中到达显式返回表达式,它将短路该隐式返回。

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

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

没有 body 块的函数以分号终止。此形式只能出现在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 函数限定符允许提供可以使用特定 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 与 "Rust" 不同的函数不支持 Rust 完全相同的展开方式。因此,在具有此类 ABI 的函数的末尾展开会导致进程中止。

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

常量函数

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

常量函数可以使用extern 函数限定符。

不允许常量函数为异步

异步函数

函数可以限定为异步,并且这也可以与 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捕获所有函数参数,包括那些未使用的参数或绑定到 _ 模式的参数。这确保了函数参数的丢弃顺序与函数不是异步时相同,只不过丢弃发生在返回的 future 被完全等待之后。

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

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

结合 asyncunsafe

声明一个既是异步的又是不安全的函数是合法的。生成的函数调用是不安全的,并且(像任何异步函数一样)返回一个 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_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) {
}