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 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!