Skip to content

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);
}

可以看到,get1get2get3都可以自动转成fn(&T) → usize类型。

Released under the MIT License