Mengapa menambahkan imp kedua mencegah paksaan deref argumen?

10

Saya menemukan masalah ini ketika mencoba menambahkan alat Add<char> for Stringke perpustakaan standar. Tapi kita bisa meniru itu dengan mudah, tanpa operator shenanigans. Kita mulai dengan ini:

trait MyAdd<Rhs> {
    fn add(self, rhs: Rhs) -> Self;
}

impl MyAdd<&str> for String {
    fn add(mut self, rhs: &str) -> Self {
        self.push_str(rhs);
        self
    }
}

Cukup sederhana. Dengan ini, kode berikut dikompilasi:

let a = String::from("a");
let b = String::from("b");
MyAdd::add(a, &b);

Perhatikan bahwa dalam kasus ini, ekspresi argumen kedua ( &b) memiliki tipe &String. Ini kemudian deref-dipaksa &strdan fungsi panggilan berfungsi.

Namun , mari kita coba tambahkan imp berikut:

impl MyAdd<char> for String {
    fn add(mut self, rhs: char) -> Self {
        self.push(rhs);
        self
    }
}

( Semuanya di Playground )

Sekarang MyAdd::add(a, &b)ungkapan di atas mengarah ke kesalahan berikut:

error[E0277]: the trait bound `std::string::String: MyAdd<&std::string::String>` is not satisfied
  --> src/main.rs:24:5
   |
2  |     fn add(self, rhs: Rhs) -> Self;
   |     ------------------------------- required by `MyAdd::add`
...
24 |     MyAdd::add(a, &b);
   |     ^^^^^^^^^^ the trait `MyAdd<&std::string::String>` is not implemented for `std::string::String`
   |
   = help: the following implementations were found:
             <std::string::String as MyAdd<&str>>
             <std::string::String as MyAdd<char>>

Mengapa demikian? Bagi saya sepertinya deref-paksaan hanya dilakukan ketika hanya ada satu kandidat fungsi. Tapi ini sepertinya salah bagiku. Kenapa aturannya seperti itu? Saya mencoba melihat melalui spesifikasi, tetapi saya belum menemukan apa pun di argumen paksaan deref.

Lukas Kalbertodt
sumber
Ini mengingatkan saya pada jawaban ini (saya menulis). Kompiler mengetahui sifat secara umum, dan ketika hanya ada satu implyang berlaku, ia dapat disatukan dengan memilih tipe argumen yang digunakan di dalamnya impl. Di T&J lain saya menggunakan kemampuan ini untuk membuat kompiler (tampaknya) memilih impldi situs panggilan, yang merupakan sesuatu yang biasanya tidak dapat dilakukan. Agaknya dalam hal ini itulah yang memungkinkannya melakukan paksaan deref. Tapi itu hanya dugaan.
trentcl
2
Berikut adalah komentar yang menyatakan bahwa jika hanya satu impl ditemukan kompiler "dengan penuh semangat mengkonfirmasi" itu, yang memungkinkan paksaan deref (antara lain) terjadi. Itu tidak terjadi untuk beberapa kandidat kandidat. Jadi saya kira itu semacam jawaban, tetapi saya masih ingin tahu lebih banyak. Bab ini dalam buku rustc mungkin membantu, tetapi sejauh yang saya tahu, itu tidak secara khusus mengatakan sesuatu tentang ini.
Lukas Kalbertodt

Jawaban:

0

Seperti yang Anda jelaskan sendiri, kompiler menangani kasus di mana hanya ada satu yang valid implsecara khusus, dan dapat menggunakannya untuk mendorong inferensi tipe:

Berikut adalah komentar yang menyatakan bahwa jika hanya satu impl ditemukan kompiler "dengan penuh semangat mengkonfirmasi" itu, yang memungkinkan paksaan deref (antara lain) terjadi. Itu tidak terjadi untuk beberapa kandidat kandidat.

Bagian kedua adalah bahwa paksaan deref hanya akan terjadi di situs di mana jenis yang diharapkan diketahui, itu tidak terjadi secara spekulatif. Lihat situs pemaksaan dalam referensi. Pemilihan imp dan inferensi jenis harus terlebih dahulu secara eksplisit menemukan yang MyAdd::add(&str)akan diharapkan, untuk mencoba memaksa argumen &str.

Jika solusi diperlukan dalam situasi ini, gunakan ekspresi seperti &*batau &b[..]atau b.as_str()untuk argumen kedua.

ramslök
sumber