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.
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.