ทำไมการเพิ่มความหมายที่สองทำให้ไม่สามารถโต้แย้งได้


10

ฉันเจอปัญหานี้เมื่อพยายามเพิ่มความหมายAdd<char> for Stringลงในไลบรารีมาตรฐาน แต่เราสามารถทำซ้ำได้อย่างง่ายดายโดยไม่ต้องมีผู้ควบคุม shenanigans เราเริ่มต้นด้วยสิ่งนี้:

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

เรียบง่ายพอสมควร ด้วยวิธีนี้รหัสต่อไปนี้รวบรวม:

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

ทราบว่าในกรณีนี้การแสดงออกอาร์กิวเมนต์ที่สอง ( &b) &Stringมีชนิด จากนั้นจะถูกบีบอัด&strและการเรียกใช้ฟังก์ชันทำงาน

อย่างไรก็ตามลองเพิ่ม impl ต่อไปนี้:

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

( ทุกอย่างบนสนามเด็กเล่น )

ตอนนี้MyAdd::add(a, &b)นิพจน์ด้านบนนำไปสู่ข้อผิดพลาดต่อไปนี้:

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

ทำไมถึงเป็นอย่างนั้น? สำหรับฉันดูเหมือนว่าการขู่เข็ญจะกระทำเมื่อมีผู้สมัครเพียงคนเดียวเท่านั้น แต่นี่ดูเหมือนผิดสำหรับฉัน ทำไมกฎจะเป็นเช่นนั้น ฉันพยายามตรวจสอบสเปค แต่ฉันไม่พบอะไรเลยในการโต้เถียง


สิ่งนี้ทำให้ฉันนึกถึงคำตอบนี้ (ฉันเขียน) คอมไพเลอร์รู้ลักษณะทั่วไปและเมื่อมีเพียงหนึ่งที่นำไปใช้ก็สามารถกระจ่างโดยเลือกอาร์กิวเมนต์ชนิดที่ใช้ในการที่impl implในคำถามอื่น ๆ ฉันใช้ความสามารถนี้เพื่อทำให้คอมไพเลอร์ (ปรากฏ) เลือกimplไซต์การโทรซึ่งเป็นสิ่งที่ปกติไม่สามารถทำได้ ในกรณีนี้น่าจะเป็นสิ่งที่ยอมให้ถูกบังคับขู่เข็ญ แต่นั่นเป็นเพียงการเดา
trentcl

2
นี่คือความคิดเห็นที่ระบุว่าหากเพียงหนึ่ง impl พบผู้แปล "กระหายยืนยัน" มันซึ่งช่วยให้การบังคับ deref (เหนือสิ่งอื่นใด) ที่จะเกิดขึ้น นั่นไม่ได้เกิดขึ้นสำหรับผู้สมัคร impl หลายคน ดังนั้นฉันเดาว่าเป็นคำตอบที่ดี แต่ฉันก็ยังรักที่จะเรียนรู้เพิ่มเติม บทนี้ในหนังสือสนิมอาจช่วยได้ แต่เท่าที่ฉันสามารถบอกได้มันไม่ได้พูดอะไรเกี่ยวกับเรื่องนี้โดยเฉพาะ
Lukas Kalbertodt

คำตอบ:


0

ตามที่คุณอธิบายตัวแปลคอมไพเลอร์จะจัดการกับกรณีที่มีเพียงหนึ่งตัวที่ถูกต้องimplเป็นพิเศษและสามารถใช้สิ่งนี้เพื่ออนุมานประเภทของไดรฟ์:

นี่คือความคิดเห็นที่ระบุว่าหากเพียงหนึ่ง impl พบผู้แปล "กระหายยืนยัน" มันซึ่งช่วยให้การบังคับ deref (เหนือสิ่งอื่นใด) ที่จะเกิดขึ้น นั่นไม่ได้เกิดขึ้นสำหรับผู้สมัคร impl หลายคน

ส่วนที่สองคือการบีบบังคับ derefจะเกิดขึ้นในไซต์ที่ทราบประเภทที่คาดไว้เท่านั้น แต่จะไม่เกิดขึ้นโดยเฉพาะ ดูเว็บไซต์บังคับในการอ้างอิง เลือก Impl และอนุมานชนิดต้องแรกอย่างชัดเจนพบว่าจะได้รับการคาดหวังที่จะพยายามบีบบังคับอาร์กิวเมนต์MyAdd::add(&str)&str

หากต้องการแก้ไขปัญหาในสถานการณ์นี้ให้ใช้นิพจน์เช่น&*bหรือ&b[..]หรือb.as_str()สำหรับอาร์กิวเมนต์ที่สอง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.