일관성(Coherence)
참고: 이 문서는 @lcnr의 노트를 바탕으로 작성되었습니다.
일관성 검사는 트레이트 impl과 고유 impl이 다른 impl과 겹치는지를 모두 검출하는 과정입니다. (참고로, 고유 impl은 impl MyStruct {}처럼 구체 타입에 대한 impl입니다.)
겹치는 트레이트 impl은 항상 오류를 발생시키지만, 겹치는 고유 impl은 같은 이름의 메서드를 가지는 경우에만 오류가 됩니다.
겹침 검사는 두 부분으로 나뉩니다. 첫째는 겹침 검사로, 컴파일러가 현재 알고 있는 트레이트 구현과 고유 구현 사이의 겹침을 찾아냅니다.
그러나 일관성 검사는 현재 알려져 있지 않더라도 다른 impl이 존재할 수 있는 경우에도 오류를 발생시킵니다. 이는 업스트림 크레이트에 하위 호환되는 방식으로 추가될 수 있는 impl과 다운스트림 크레이트의 impl에 영향을 미칩니다. 이것을 고아 검사(Orphan check)라고 합니다.
겹침 검사
겹침 검사는 고유 impl과 트레이트 impl 모두에 대해 수행됩니다. 두 검사는 같은 겹침 검사 코드를 사용하지만, 실제로는 별개의 두 분석으로 수행됩니다. 겹침 검사는 항상 구현을 쌍으로 묶어 서로 비교합니다.
고유 impl 블록에 대한 겹침 검사는 fn check_item(coherence/inherent_impls_overlap.rs 내)을 통해 수행되며, 여기서 이 검사가 (적어도 작은 n에 대해서는) 실제로 impl 사이에 n^2번의 비교를 수행한다는 것을 매우 분명하게 확인할 수 있습니다.
트레이트의 경우, 부모와 겹치는 특수화 impl을 처리하기 위해 현재 이 검사는 특수화 그래프를 구축하는 과정의 일부로 수행되지만, 이는 향후 바뀔 수 있습니다.
두 경우 모두 모든 impl 쌍에 대해 겹침 여부를 검사합니다.
겹침은 때때로 부분적으로 허용됩니다.
- 마커 트레이트의 경우
- 특수화를 사용하는 경우
일반적으로는 허용되지 않습니다.
겹침 검사에는 여러 모드가 있습니다(OverlapMode 참조). 중요한 것은 명시적 부정 impl 검사와 암시적 부정 impl 검사가 있다는 점입니다. 두 검사 모두 겹침이 절대로 불가능하다는 것을 증명하려고 시도합니다.
명시적 부정 impl 검사
이 검사는 impl_intersection_has_negative_obligation에서 수행됩니다.
이 검사는 부정 트레이트 구현을 찾으려고 시도합니다. 예를 들면 다음과 같습니다.
#![allow(unused)]
fn main() {
struct MyCustomErrorType;
// both in your own crate
impl From<&str> for MyCustomErrorType {}
impl<E> From<E> for MyCustomErrorType where E: Error {}
}
이 예제에서는 MyCustomErrorType: From<&str>와 MyCustomErrorType: From<?E>를 얻게 되며, 이로부터 ?E = &str이 도출됩니다.
따라서 이 두 구현은 겹치게 됩니다. 그러나 libstd는 &str: !Error를 제공하므로 &str: Error의 긍정 구현이 절대 존재하지 않을 것임을 보장하며, 따라서 겹침이 없습니다.
이러한 종류의 부정 impl 검사를 위해서는 명시적인 부정 구현이 제공되어야 한다는 점에 유의하십시오. 이 기능은 현재 안정화되지 않았습니다.
암묵적 부정 impl 검사
이 검사는 impl_intersection_has_impossible_obligation에서 수행되며, 부정 트레이트 구현에 의존하지 않고 안정화되어 있습니다.
다음과 같은 코드가 있다고 가정해 봅시다.
#![allow(unused)]
fn main() {
impl From<MyLocalType> for Box<dyn Error> {} // in your own crate
impl<E> From<E> for Box<dyn Error> where E: Error {} // in std
}
이 경우 Box<dyn Error>: From<MyLocalType>와 Box<dyn Error>: From<?E>가 나오고, 그 결과 ?E = MyLocalType이 됩니다.
여러분의 크레이트에는 MyLocalType: Error가 없으며, 하위 크레이트는 MyLocalType(원격 타입)에 대해 Error(원격 트레이트)를 구현할 수 없습니다. 따라서 이 두 impl은 겹치지 않습니다. 중요한 점은 impl !Error for MyLocalType가 없더라도 이 방식이 동작한다는 것입니다.