Rust在值上调用方法之间是否有任何区别,像这样:
struct A { e: u32 }
impl A {
fn show(&self) {
println!("{}",self.e)
}
}
fn main() {
A { e: 0 }.show();
}
...并按类型调用它,就像这样:
fn main() {
A::show(&A { e: 0 })
}
Rust在值上调用方法之间是否有任何区别,像这样:
struct A { e: u32 }
impl A {
fn show(&self) {
println!("{}",self.e)
}
}
fn main() {
A { e: 0 }.show();
}
...并按类型调用它,就像这样:
fn main() {
A::show(&A { e: 0 })
}
摘要:最重要的区别是 u 通用 f 联合 c 所有 s 语法(UFCS)比方法调用语法更明确。
对于UFCS,基本上没有任何疑问要调用什么函数(对于trait方法,UFCS的形式仍然更长,但是现在让我们忽略它)。另一方面,方法调用语法需要在编译器中进行更多工作才能确定要调用的方法以及如何调用它。这主要体现在两件事上:
self
),并可能使用类型强制使呼叫正常进行。
让我们看一下这个示例,以了解对接收器类型的强制转换:
struct Foo;
impl Foo {
fn on_ref(&self) {}
fn on_mut_ref(&mut self) {}
fn on_value(self) {}
}
fn main() {
let reference = &Foo; // type `&Foo`
let mut_ref = &mut Foo; // type `&mut Foo`
let mut value = Foo; // type `Foo`
// ...
}
因此,我们有三种采用Foo
,&Foo
和&mut Foo
接收者的方法,并且我们有三种类型的变量。让我们尝试使用方法调用语法和UFCS的所有9种组合。
UFCS
Foo::on_ref(reference);
//Foo::on_mut_ref(reference); error: mismatched types
//Foo::on_value(reference); error: mismatched types
//Foo::on_ref(mut_ref); error: mismatched types
Foo::on_mut_ref(mut_ref);
//Foo::on_value(mut_ref); error: mismatched types
//Foo::on_ref(value); error: mismatched types
//Foo::on_mut_ref(value); error: mismatched types
Foo::on_value(value);
我们可以看到,只有类型正确的调用才能成功。为了使其他调用正常工作,我们必须在参数前面手动添加&
或&mut
或*
。这是所有函数参数的标准行为。
方法调用语法
reference.on_ref();
//reference.on_mut_ref(); error: cannot borrow `*reference` as mutable
//reference.on_value(); error: cannot move out of `*reference`
mut_ref.on_ref();
mut_ref.on_mut_ref();
//mut_ref.on_value(); error: cannot move out of `*mut_ref`
value.on_ref();
value.on_mut_ref();
value.on_value();
只有三个方法调用导致错误,而其他方法成功。在这里,编译器会自动插入deref(解引用)或autoref(添加引用)强制,以使调用起作用。另外请注意,这三个错误不是“类型不匹配”错误:编译器已经尝试正确调整类型,但这会导致其他错误。
还有一些其他强制性的内容:
Unsize
trait描述。允许您在数组上调用切片方法,并将类型强制转换为它们实现的特征的特征对象。
Deref
trait 高级取消强制。例如,这使您可以在Vec
上调用slice方法。
在编写lhs.method_name()
时,方法method_name
可以是lhs
类型的固有方法,也可以属于范围内(导入)的特征。编译器必须弄清楚要调用哪一个,并且为此有许多规则。当进入细节时,这些规则实际上确实很复杂,并且可能导致某些令人惊讶的行为。幸运的是,大多数程序员将永远不必处理它,并且它在大多数情况下“有效”。
要大致了解其工作方式,编译器将使用找到的第一种方法按顺序尝试以下操作。
method_name
的固有方法,其中接收方类型恰好适合(不需要强制)?method_name
的特征方法,其中接收方类型完全适合(不需要强制)?method_name
的固有方法? (将执行强制类型)method_name
的特征方法? (将执行强制类型)(再次,请注意,这仍然是一种简化。例如,不同类型的强制比其他类型的强制更可取。)
这显示了大多数程序员都知道的一条规则:固有方法比特质方法具有更高的优先级。但是,一个更重要的因素是,接收器类型是否完全适合这一事实,这是一个未知数。有一个测验很好地证明了这一点:Rust Quiz #23。有关确切的方法解析算法的更多详细信息,请参见this StackOverflow answer。
这组规则实际上可以对API进行大量更改以破坏更改。我们目前必须处理该in the attempt to add an IntoIterator
impl for arrays。
另一个(可能非常明显)的区别是,对于方法调用语法,不必导入类型名称。
除此之外,还有必要指出两种语法没有什么不同: