[rust] `std :: mem :: drop`이 상위 특성 트레이드의 클로저 | _ | ()와 정확히 동일하지 않은 이유는 무엇입니까?

구현은 std::mem::drop다음과 같습니다.

pub fn drop<T>(_x: T) { }

따라서, 폐쇄 |_| ()( 구체적 으로 화장실 폐쇄 라고도 함 )가 drop양방향으로 잠재적으로 1 : 1로 대체 될 것으로 기대합니다 . 그러나 아래 코드 drop는 화장실 변수가 변하는 반면 함수의 매개 변수에 더 높은 순위의 특성과 호환되지 않음을 보여줍니다 .

fn foo<F, T>(f: F, x: T)
where
    for<'a> F: FnOnce(&'a T),
{
    dbg!(f(&x));
}

fn main() {
    foo(|_| (), "toilet closure"); // this compiles
    foo(drop, "drop"); // this does not!
}

컴파일러의 오류 메시지 :

error[E0631]: type mismatch in function arguments
  --> src/main.rs:10:5
   |
1  | fn foo<F, T>(f: F, x: T)
   |    ---
2  | where
3  |     for<'a> F: FnOnce(&'a T),
   |                ------------- required by this bound in `foo`
...
10 |     foo(drop, "drop"); // this does not!
   |     ^^^
   |     |
   |     expected signature of `for<'a> fn(&'a _) -> _`
   |     found signature of `fn(_) -> _`

error[E0271]: type mismatch resolving `for<'a> <fn(_) {std::mem::drop::<_>} as std::ops::FnOnce<(&'a _,)>>::Output == ()`
  --> src/main.rs:10:5
   |
1  | fn foo<F, T>(f: F, x: T)
   |    ---
2  | where
3  |     for<'a> F: FnOnce(&'a T),
   |                ------------- required by this bound in `foo`
...
10 |     foo(drop, "drop"); // this does not!
   |     ^^^ expected bound lifetime parameter 'a, found concrete lifetime

즉 고려 drop어떤 크기에 대한 가정 제네릭 T, 그것은 “더 일반적인”서명이 있다는 불합리한 소리 fn(_) -> _와 호환되지 않습니다 for<'a> fn (&'a _) -> _. 컴파일러가 drop여기 서명을 인정하지 않는 이유는 무엇입니까? 변기 덮개를 대신 사용하면 어떻게 달라 집니까?



답변

문제의 핵심은 drop단일 함수가 아니라 각각 특정 유형을 삭제하는 매개 변수화 된 함수 세트라는 것입니다. 더 높은 순위의 특성 한계 (이하 hrtb)를 만족 시키려면 주어진 수명이있는 유형을 동시에 참조 할 수 있는 단일 함수가 필요합니다 .


우리는 drop일반적인 함수의 전형적인 예제로 사용할 것이지만이 모든 것이 더 일반적으로 적용됩니다. 참조 코드는 다음과 같습니다 fn drop<T>(_: T) {}..

개념적 drop으로 단일 함수가 아니라 가능한 모든 유형에 대해 하나의 함수입니다 T. 특정 인스턴스 drop는 단일 유형의 인수 만 사용합니다. 이것을 단형 화 라고 합니다. 다른이 경우 T에 사용되는 drop, 다른 버전은 drop컴파일됩니다. 따라서 일반 함수를 인수로 전달할 수없고 해당 함수를 전체 일반으로 사용할 수 없습니다 ( 이 질문 참조 ).

반면에 같은 함수 fn pass(x: &i32) -> &i32 {x}는 hrtb를 만족시킵니다 for<'a> Fn(&'a i32) -> &'a i32. 달리 drop, 우리는이 하나의 기능이 동시에 만족 Fn(&'a i32) -> &'a i32에 대한 모든 수명을 'a. 이것은 pass사용 방법에 반영 됩니다.

fn pass(x: &i32) -> &i32 {
    x
}

fn two_uses<F>(f: F)
where
    for<'a> F: Fn(&'a i32) -> &'a i32, // By the way, this can simply be written
                                       // F: Fn(&i32) -> &i32 due to lifetime elision rules.
                                       // That applies to your original example too.
{
    {
        // x has some lifetime 'a
        let x = &22;
        println!("{}", f(x));
        // 'a ends around here
    }
    {
        // y has some lifetime 'b
        let y = &23;
        println!("{}", f(y));
        // 'b ends around here
    }
    // 'a and 'b are unrelated since they have no overlap
}

fn main() {
    two_uses(pass);
}

(운동장)

예에서, 수명 'a과는 'b서로 아무 관계가 없습니다 : 아니 완전히 다른를 포함한다. 따라서 여기에는 일종의 하위 유형이 없습니다. 하나의 인스턴스 pass는 실제로 서로 관련이없는 두 개의 다른 수명으로 사용됩니다.

이것이 drop만족하지 않는 이유 for<'a> FnOnce(&'a T)입니다. 특정 인스턴스는 수명을 drop 번만 커버 할 수 있습니다 (하위 유형 무시). 우리가 통과하는 경우 에 (약간의 서명 변화와 컴파일러가 우리를하자 가정으로) 위의 예에서, 일부 특정 수명 선택해야 하고 인스턴스 의 범위에있는 것입니다 일부 콘크리트의 수명을 . 이 함수는 단일 수명에만 적용되므로 관련이없는 두 개의 수명에는 사용할 수 없습니다.droptwo_uses'adroptwo_usesFn(&'a i32)'a'a

그렇다면 왜 화장실 폐쇄가 hrtb를 얻습니까? 클로저에 대한 타입을 유추 할 때, 예상되는 타입이 더 높은 순위의 특성 한계가 필요하다는 것을 암시한다면 , 컴파일러는 하나를 적합 시키려고 시도 할 것 입니다. 이 경우 성공합니다.


이슈 # 41078 은 이것과 밀접한 관련이 있으며, 특히 여기 에서 eddyb의 의견 은 본질적으로 위의 설명을 제공합니다 (일반적인 기능보다는 폐쇄의 맥락에서). 문제 자체는 현재 문제를 해결하지 못합니다. 변기 클로저를 사용하기 전에 변수에 할당하면 어떻게됩니까 (다시 시도하십시오!).

앞으로 상황이 변할 수도 있지만, 일반 함수가 어떻게 형태 화되는지에 대해서는 상당히 큰 변화가 필요할 것입니다.


답변

즉, 두 줄 모두 실패해야합니다. 그러나 hrtb 수명을 처리하는 오래된 방식의 한 단계, 즉 누출 검사 는 현재 일부 건전성 문제가 있으므로 rustc(잘못된) 하나를 수락하고 다른 하나는 꽤 나쁜 오류 메시지로 남겨 둡니다.

로 누출 검사를 비활성화하면 rustc +nightly -Zno-leak-check보다 합리적인 오류 메시지가 표시됩니다.

error[E0308]: mismatched types
  --> src/main.rs:10:5
   |
10 |     foo(drop, "drop");
   |     ^^^ one type is more general than the other
   |
   = note: expected type `std::ops::FnOnce<(&'a &str,)>`
              found type `std::ops::FnOnce<(&&str,)>`

이 오류에 대한 나의 해석은 함수 &x본문에 해당 본문에 foo한정된 스코프 수명 만 있기 때문에 특성 한계에 필요한 범용 정량화를 f(&x)만족시킬 수없는 동일한 스코프 수명을 갖는다는 것 for<'a>입니다.

여기에 제시된 질문은 이슈 # 57642 와 거의 동일하며 여기 에는 두 개의 대조 부분이 있습니다.

hrtb 수명을 처리하는 새로운 방법은 소위 유니버스를 사용하는 것 입니다. Niko에는 유니버스로 누출 검사를 처리하기위한 WIP 가 있습니다. 이 새로운 체제 하에서, 위에 링크 된 이슈 # 57642의 두 부분 모두 훨씬 더 명확한 진단으로 실패 한다고한다 . 그때까지 컴파일러가 예제 코드를 올바르게 처리 할 수 ​​있다고 생각합니다.


답변