Saya menemukan masalah ini ketika mencoba menambahkan alat Add<char> for String
ke 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 &str
dan 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
}
}
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.
sumber
impl
yang berlaku, ia dapat disatukan dengan memilih tipe argumen yang digunakan di dalamnyaimpl
. Di T&J lain saya menggunakan kemampuan ini untuk membuat kompiler (tampaknya) memilihimpl
di situs panggilan, yang merupakan sesuatu yang biasanya tidak dapat dilakukan. Agaknya dalam hal ini itulah yang memungkinkannya melakukan paksaan deref. Tapi itu hanya dugaan.Jawaban:
Seperti yang Anda jelaskan sendiri, kompiler menangani kasus di mana hanya ada satu yang valid
impl
secara khusus, dan dapat menggunakannya untuk mendorong inferensi tipe: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
&*b
atau&b[..]
ataub.as_str()
untuk argumen kedua.sumber