Appearance
5.4 完全限定语法
Fully Qualified Syntax 提供一种无歧义的函数调用语法,允许程序员精确地指定想调用的是那个函数。以前也叫 UFCS(Universal Function Call Syntax),也就是所谓的“统一函数调用语法”。 这个语法可以允许使用类似的写法精确调用任何方法,包括成员方法和静态方法。其他一切函数调用语法都是它的某种简略形式。 它的具体写法为<T as TraitName>::item
。
示例如下:
rust
trait Cook {
fn start(&self);
}
trait Wash {
fn start(&self);
}
struct Chef;
impl Cook for Chef {
fn start(&self) { println!("Cook::start");}
}
impl Wash for Chef {
fn start(&self) { println!("Wash::start");}
}
fn main() {
let me = Chef;
me.start();
}
我们定义了两个 trait,它们的start()
函数有同样方法签名。
如果一个类型同时实现了这两个 trait,那么如果我们使用variable.start()
这样的语法执行方法调用的话,就会出现歧义,编译器不知道你具体想调用哪个方法,编译错误信息为“multiple applicable items in scope”。
这时候,我们就有必要使用完整的函数调用语法来进行方法调用,只有这样写,才能清晰明白且无歧义地表达清楚期望调用的是哪个函数:
rust
fn main() {
let me = Chef;
// 函数名字使用更完整的 path 来指定,同时,self 参数需要显式传递
<Cook>::start(&me);
<Chef as Wash>::start(&me);
}
由此我们也可以看到,所谓的“成员方法”也没什么特殊之处,它跟普通的静态方法的唯一区别是,第一个参数是self
,而这个self
只是一个普通的函数参数而已。只不过这种成员方法也可以通过变量加小数点的方式调用。变量加小数点的调用方式在大部分情况下看起来更简单更美观,完全可以视为一种语法糖。
需要注意的是,通过小数点语法调用方法调用,有一个“隐藏着”的“取引用”步骤。虽然我们看起来源代码长的是这个样子me.start()
,但是大家心里要清楚,真正传递给start()
方法的参数是&me
而不是me
,这一步是编译器自动帮我们做的。不论这个方法接受的self
参数究竟是Self
、&Self
还是&mut Self
,最终在源码上,我们都是统一的写法:variable.method()
。而如果用 UFCS 语法来调用这个方法,我们就不能让编译器帮我们自动取引用了,必须手动写清楚。
下面用一个示例演示一下成员方法和普通函数其实没什么本质区别。
rust
struct T(usize);
impl T {
fn get1(&self) -> usize {self.0}
fn get2(&self) -> usize {self.0}
}
fn get3(t: &T) -> usize { t.0 }
fn check_type( _ : fn(&T)->usize ) {}
fn main() {
check_type(T::get1);
check_type(T::get2);
check_type(get3);
}
可以看到,get1
、get2
和get3
都可以自动转成fn(&T) → usize
类型。