rust-quiz:005-trait-resolution-hrtb.rs

godme發表於2022-07-08
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 協議》,轉載必須註明作者和本文連結

相關文章