rust-quiz:005-trait-resolution-hrtb.rs
题目
trait Trait {
fn p(self);
}
impl<T> Trait for fn(T) {
fn p(self) {
print!("1");
}
}
impl<T> Trait for fn(&T) {
fn p(self) {
print!("2");
}
}
fn f(_: u8) {}
fn g(_: &u8) {}
fn main() {
let a: fn(_) = f;
let b: fn(_) = g;
let c: fn(&_) = g;
a.p();
b.p();
c.p();
}
关键
HRTBs
有点相像,但是不是。
这里顺便解释一下:独属于自身的方法声明绑定。where for<'a> F: Fn(&'a (u8, u16)) -> &'a u8
一般在方法的内部,如果需要生命周期参数,都可以直接声明。
但是对于Fn
,有时候时候调用是不确定的。
它的调用完全可以独立于对象之外,只在乎方法入参和出参,以及内部操作。
这个时候使用for
进行单独的声明周期绑定就可以达到该目的。T
泛型T
的意思,可不仅仅是针对拥有所有权的对象,而是Object
。
引用,本身也是一种对象,毫不客气的说, \&T \in T。
审题
这道题里面都为fn
实现了Trait
,当然,这并不是HRTBs
。
如果是fn(T)
的签名,会打印1
。
如果是fn(&T)
的签名,会打印2
。
后续通过匹配的方式,让编译器自动的推导对应的声明类型,从而触发打印。
很明显能看出来
f
的参数类型是u8
g
的参数类型是&u8
这里主要看的就是如何匹配的泛型了。
let a: fn(_) = f;
这个毫无疑问,匹配的_
推导出来必然是T
。
这里T = u8
, 因此打印1
。let b: fn(_) = g;
这是比较让人犯迷糊的,因为g
的入参是&u8
。
但是,对于b
而言,匹配方式是_
,只要有入参,它就匹配,就是T
。
这里T = &u8
,因此打印1
。let c: fn(&_) = g;
这里可以看到,这个匹配模式是&_
,对应匹配的是&u8
。
这里的_
提取出来的是T=u8
,但是作为整体而言,也就是&T
。
也就是说,这里强制表达的是,入参必须是一个引用。
当明确入参是一个引用的时候,就会打印2
。
题解
因此结果不言而喻112
。
这里的匹配和其他的匹配不太一样,它是经过推导之后在进行的匹配。
尽管我们的Trait
实现中已经标明了泛型类型,但是最终还需要匹配函数签名。
作为一个整体进行解析,最重要的就是声明的准确性,不能过于依赖编译器的自动推理。
没有明确的指定,\&T \in T可能会出现预料之外的情况。
本作品采用《CC 协议》,转载必须注明作者和本文链接