`Std :: mem :: drop` neden üst sıra özellik sınırlarındaki kapatma | _ | () ile tam olarak aynı değil?


13

Uygulamasının std::mem::dropaşağıdaki gibi belgelenmiştir:

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

Bu nedenle, kapamanın |_| ()(konuşma dilinde tuvalet kapağı olarak bilinir ) dropher iki yönde de potansiyel bir 1: 1 değiştirme olmasını beklerim. Bununla birlikte, aşağıdaki kod drop, işlevin parametresine bağlı daha yüksek sıralı bir özellikle uyumlu olmadığını gösterirken , tuvalet kapatmasıdır.

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!
}

Derleyicinin hata mesajı:

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

dropHerhangi bir boyuta göre jenerik olduğu düşünüldüğünde T, "daha genel" imzanın fn(_) -> _uyumlu olmadığı mantıksız geliyor for<'a> fn (&'a _) -> _. Derleyici neden dropburada imzayı kabul etmiyor ve tuvalet kapağı yerine yerleştirildiğinde onu farklı kılan nedir?

Yanıtlar:


4

Sorunun özü, droptek bir işlev değil, her birinin belirli bir türü bırakan parametreli bir işlevler kümesi olmasıdır. Daha yüksek dereceli bir özellik sınırını (bundan sonra hrtb olarak anılacaktır) karşılamak için, belirli bir kullanım ömrüne sahip olan bir türe aynı anda referans alabilen tek bir işleve ihtiyacınız olacaktır .


dropTipik bir genel işlev örneğimiz olarak kullanacağız , ancak tüm bunlar daha genel olarak da geçerlidir. İşte başvuru için kod: fn drop<T>(_: T) {}.

Kavramsal olarak, droptek bir işlev değil, olası her tür için bir işlevdir T. Belirli bir örneği dropyalnızca tek bir türün bağımsız değişkenlerini alır. Buna monomorfizasyon denir . Farklı bir Tile kullanılırsa drop, farklı bir sürümü dropderlenir. Bu nedenle genel bir işlevi bağımsız değişken olarak iletemez ve bu işlevi genel olarak kullanamazsınız ( bu soruya bakın )

Öte yandan, benzeri bir işlev fn pass(x: &i32) -> &i32 {x}hrtb'yi tatmin eder for<'a> Fn(&'a i32) -> &'a i32. Bunun aksine drop, her yaşam için aynı anda tatmin eden tek bir fonksiyonumuz var . Bu nasıl kullanılabileceğine yansır .Fn(&'a i32) -> &'a i32'apass

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);
}

(oyun alanı)

Örnekte, yaşam süreleri 'ave 'bbirbirleriyle hiçbir ilişkisi yoktur: ikisi de diğerini tamamen kapsamaz. Yani burada bir tür alt tipleme yok. Tek bir örneği passgerçekten iki farklı, ilişkisiz yaşamda kullanılıyor.

Bu yüzden droptatmin olmuyor for<'a> FnOnce(&'a T). Belirli bir örneği dropyalnızca bir kullanım ömrünü kapsayabilir (alt türü yok sayarak). Biz geçti Eğer dropiçine two_uses(hafif imza değişiklikleri ve derleyici bize varsayarak birlikte) Yukarıdaki örnekten, bazı belirli ömrünü seçmek zorunda kalacak 'ave örneğini dropkapsamında two_usesolacağını Fn(&'a i32)bazıları için beton ömrü 'a. İşlev yalnızca tek bir kullanım ömrü için geçerli olacağından, işlevsiz 'aiki yaşam süresi ile kullanılması mümkün olmaz.

Peki neden tuvalet kapatması bir hrtb alıyor? Bir kapatma türü için çıkarım yapılırken, beklenen tür daha üst sıradaki bir özellik sınırının gerekli olduğunu ima ederse , derleyici bir uyum sağlamaya çalışır . Bu durumda başarılı olur.


Sayı 41078 bununla yakından ilgilidir ve özellikle eddyb'in buradaki yorumu esasen yukarıdaki açıklamayı verir (sıradan işlevler yerine kapatma bağlamında olsa da). Sorunun kendisi mevcut sorunu ele almıyor. Bunun yerine, tuvalet kapağını kullanmadan önce bir değişkene atarsanız ne olacağını ele alır (deneyin!).

Durumun gelecekte değişmesi mümkündür, ancak genel fonksiyonların monomorfize edilmesinde oldukça büyük bir değişiklik gerekecektir.


4

Kısacası, her iki satır da başarısız olmalıdır. Ancak, hrtb ömürlerini, yani sızıntı kontrolünü eski yöntemle bir adım attığından , şu anda bazı sağlamlık sorunu var, rustc(yanlış) birini kabul ediyor ve diğerini oldukça kötü bir hata mesajıyla bırakıyor.

Sızıntı kontrolünü devre dışı bırakırsanız rustc +nightly -Zno-leak-check, daha mantıklı bir hata mesajı görebilirsiniz:

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,)>`

Bu hatayı yorumlamam &x, foofonksiyonun gövdesinde sadece söz konusu gövdeyle sınırlı bir kapsam ömrüne sahip olması, bu nedenle f(&x)aynı zamanda for<'a>bağlı özellik tarafından gerekli olan evrensel nicelemeyi karşılayamayan aynı kapsam ömrüne sahip olmasıdır .

Burada sunduğunuz soru , aynı zamanda iki zıt parçaya sahip olan # 57642 sayısıyla hemen hemen aynı .

Hrtb yaşamlarını işlemenin yeni yolu, sözde evrenler kullanmaktır . Niko'nun kaçak kontrolünü evrenlerle ele almak için bir WIP'si var . Bu yeni rejim altında, yukarıdaki 57642 sayılı sorunun her iki bölümünün hepsinin çok daha açık teşhislerle başarısız olduğu söylenmektedir . Derleyici o zamana kadar örnek kodunuzu doğru bir şekilde işleyebilmelidir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.