方法调用表达式

语法
MethodCallExpression :
   表达式 . 路径表达式段 (调用参数? )

一个方法调用由一个表达式(接收者)后跟一个点、一个表达式路径段和一个带括号的表达式列表组成。方法调用被解析为特定 trait 上的关联方法,如果左侧的精确 self 类型已知,则静态分发到方法;如果左侧表达式是间接的 trait 对象,则动态分发。

#![allow(unused)]
fn main() {
let pi: Result<f32, _> = "3.14".parse();
let log_pi = pi.unwrap_or(1.0).log(2.72);
assert!(1.14 < log_pi && log_pi < 1.15)
}

在查找方法调用时,为了调用方法,接收者可能会被自动解引用或借用。这需要比其他函数更复杂的查找过程,因为可能有许多可能调用的方法。使用以下过程

第一步是构建候选接收者类型列表。通过重复解引用接收者表达式的类型来获得这些类型,将遇到的每个类型添加到列表中,然后在最后尝试非定型强制转换,如果成功,则添加结果类型。然后,对于每个候选类型 T,在 T 之后立即将 &T&mut T 添加到列表中。

例如,如果接收者的类型为 Box<[i32;2]>,那么候选类型将是 Box<[i32;2]>&Box<[i32;2]>&mut Box<[i32;2]>[i32; 2](通过解引用)、&[i32; 2]&mut [i32; 2][i32](通过非定型强制转换)、&[i32],最后是 &mut [i32]

然后,对于每个候选类型 T,在以下位置搜索具有该类型接收者的可见方法

  1. T 的固有方法(直接在 T 上实现的方法)。
  2. T 实现的可见 trait 提供的任何方法。如果 T 是类型参数,则首先查找 trait 边界在 T 上提供的方法。然后查找范围内的所有剩余方法。

注意:查找是按顺序对每种类型完成的,这有时会导致令人惊讶的结果。下面的代码将打印 “In trait impl!”,因为首先查找 &self 方法,所以在找到结构体的 &mut self 方法之前找到了 trait 方法。

struct Foo {}

trait Bar {
  fn bar(&self);
}

impl Foo {
  fn bar(&mut self) {
    println!("In struct impl!")
  }
}

impl Bar for Foo {
  fn bar(&self) {
    println!("In trait impl!")
  }
}

fn main() {
  let mut f = Foo{};
  f.bar();
}

如果这导致多个可能的候选方法,则会发生错误,并且必须将接收者转换为适当的接收者类型以进行方法调用。

此过程不考虑接收者的可变性或生命周期,也不考虑方法是否为 unsafe。一旦查找到方法,如果由于其中一个(或多个)原因而无法调用该方法,则结果将是编译器错误。

如果达到某个步骤,其中存在多个可能的方法,例如在考虑通用方法或 trait 相同时,则会发生编译器错误。这些情况需要用于方法和函数调用的消除歧义的函数调用语法

版本差异:在 2021 版本之前,在搜索可见方法期间,如果候选接收者类型是数组类型,则会忽略标准库 IntoIterator trait 提供的方法。

用于此目的的版本由表示方法名称的 token 确定。

此特殊情况将来可能会被删除。

警告: 对于 trait 对象,如果存在与 trait 方法同名的固有方法,则当尝试在方法调用表达式中调用该方法时,将产生编译器错误。相反,您可以使用消除歧义的函数调用语法来调用该方法,在这种情况下,它会调用 trait 方法,而不是固有方法。没有办法调用固有方法。只是不要在 trait 对象上定义与 trait 方法同名的固有方法,您就会没事的。