方法调用表达式
语法
MethodCallExpression :
表达式.
PathExprSegment(
CallParams?)
方法调用 由一个表达式(接收者)组成,后面跟着一个点、一个表达式路径段和一个括号内的表达式列表。方法调用被解析为特定特征上的关联方法,如果已知左侧的确切 self
类型,则静态分派到方法,如果左侧表达式是间接特征对象,则动态分派。
#![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
,在以下位置搜索具有该类型接收者的可见方法
T
的固有方法(直接在T
上实现的方法)。- 由
T
实现的可见特征提供的任何方法。如果T
是类型参数,则首先查找T
上的特征边界提供的方法。然后查找作用域中的所有剩余方法。
注意:查找是按顺序针对每种类型完成的,这有时会导致令人惊讶的结果。下面的代码将打印“在特征实现中!”,因为首先查找
&self
方法,所以在找到结构体的&mut self
方法之前找到了特征方法。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
。一旦查找方法,如果由于一个(或多个)原因无法调用它,则结果是编译器错误。
如果在某个步骤中存在多个可能的方法,例如泛型方法或特征被认为是相同的,那么这将是一个编译错误。这些情况需要使用 消除歧义的函数调用语法 来调用方法和函数。
版本差异:在 2021 版之前,在搜索可见方法期间,如果候选接收者类型是 数组类型,则会忽略标准库
IntoIterator
特征提供的方法。用于此目的的版本由表示方法名称的标记确定。
此特殊情况将来可能会被移除。
警告: 对于 特征对象,如果存在与特征方法同名的固有方法,则在尝试在方法调用表达式中调用该方法时,编译器会报错。作为替代,您可以使用 消除歧义的函数调用语法 调用该方法,在这种情况下,它会调用特征方法,而不是固有方法。无法调用固有方法。只要不在与特征方法同名的特征对象上定义固有方法,就不会有问题。