เมื่อใดจึงเหมาะสมที่จะใช้ประเภทที่เกี่ยวข้องกับประเภททั่วไป


111

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

RFC ที่นำประเภทที่เกี่ยวข้องพูดว่า:

RFC นี้ชี้แจงการจับคู่ลักษณะโดย:

  • การปฏิบัติต่อพารามิเตอร์ชนิดลักษณะทั้งหมดเป็นประเภทอินพุตและ
  • การให้บริการประเภทที่เกี่ยวข้องซึ่งเป็นประเภทเอาท์พุท

RFC ใช้โครงสร้างกราฟเป็นตัวอย่างที่สร้างแรงจูงใจและยังใช้ในเอกสารประกอบแต่ฉันจะยอมรับว่าไม่เห็นคุณค่าประโยชน์ของเวอร์ชันประเภทที่เกี่ยวข้องอย่างเต็มที่ในเวอร์ชัน type-parameterized สิ่งสำคัญคือdistanceวิธีการไม่จำเป็นต้องสนใจเกี่ยวกับEdgeประเภท นี่เป็นสิ่งที่ดี แต่ดูเหมือนจะค่อนข้างตื้นสำหรับเหตุผลที่มีประเภทที่เกี่ยวข้องเลย

ฉันพบว่าประเภทที่เกี่ยวข้องนั้นค่อนข้างใช้งานง่ายในทางปฏิบัติ แต่ฉันพบว่าตัวเองกำลังลำบากเมื่อต้องตัดสินใจว่าควรใช้ที่ไหนและเมื่อใดใน API ของฉันเอง

เมื่อเขียนโค้ดฉันควรเลือกประเภทที่เกี่ยวข้องกับพารามิเตอร์ประเภททั่วไปเมื่อใดและเมื่อใดที่ฉันควรทำตรงกันข้าม

คำตอบ:


80

นี้จะสัมผัสในขณะนี้ในรุ่นที่สองของสนิมโปรแกรมภาษา อย่างไรก็ตามเรามาดูเพิ่มเติมกันดีกว่า

ให้เราเริ่มต้นด้วยตัวอย่างที่ง่ายกว่า

เมื่อใดจึงเหมาะสมที่จะใช้วิธีลักษณะ?

มีหลายวิธีในการเชื่อมโยงล่าช้า :

trait MyTrait {
    fn hello_word(&self) -> String;
}

หรือ:

struct MyTrait<T> {
    t: T,
    hello_world: fn(&T) -> String,
}

impl<T> MyTrait<T> {
    fn new(t: T, hello_world: fn(&T) -> String) -> MyTrait<T>;

    fn hello_world(&self) -> String {
        (self.hello_world)(self.t)
    }
}

โดยไม่คำนึงถึงกลยุทธ์การนำไปใช้งาน / ประสิทธิภาพใด ๆ ทั้งสองข้อความที่ตัดตอนมาข้างต้นทำให้ผู้ใช้สามารถระบุได้อย่างมีไดนามิกว่าhello_worldควรปฏิบัติอย่างไร

หนึ่งความแตกต่าง (ความหมาย) คือการที่ผู้traitค้ำประกันการใช้งานที่สำหรับประเภทได้รับTการดำเนินการtrait, hello_worldมักจะมีพฤติกรรมเดียวกันในขณะที่structการดำเนินการอนุญาตให้มีพฤติกรรมที่แตกต่างกันในแต่ละตัวอย่างพื้นฐาน

การใช้วิธีจะเหมาะสมหรือไม่ขึ้นอยู่กับลักษณะการใช้งาน!

เมื่อใดจึงเหมาะสมที่จะใช้ประเภทที่เกี่ยวข้อง

เช่นเดียวกับtraitวิธีการข้างต้นประเภทที่เกี่ยวข้องคือรูปแบบของการผูกแบบล่าช้า (แม้ว่าจะเกิดขึ้นในการคอมไพล์) ซึ่งอนุญาตให้ผู้ใช้traitระบุอินสแตนซ์ที่กำหนดว่าจะใช้ประเภทใดแทน ไม่ใช่วิธีเดียว (ดังนั้นคำถาม):

trait MyTrait {
    type Return;
    fn hello_world(&self) -> Self::Return;
}

หรือ:

trait MyTrait<Return> {
    fn hello_world(&Self) -> Return;
}

เทียบเท่ากับการผูกปลายของวิธีการข้างต้น:

  • คนแรกบังคับว่าสำหรับสิ่งที่ระบุSelfมีหนึ่งเดียวที่Returnเกี่ยวข้อง
  • คนที่สองแทนช่วยให้การดำเนินการMyTraitสำหรับSelfหลาย ๆReturn

รูปแบบใดเหมาะสมกว่าขึ้นอยู่กับว่าเหมาะสมที่จะบังคับใช้ Unicity หรือไม่ ตัวอย่างเช่น:

  • Deref ใช้ประเภทที่เกี่ยวข้องเนื่องจากไม่มีความเป็นเอกภาพคอมไพเลอร์จะบ้าคลั่งในระหว่างการอนุมาน
  • Add ใช้ประเภทที่เกี่ยวข้องเนื่องจากผู้เขียนคิดว่าการกำหนดอาร์กิวเมนต์ทั้งสองจะมีประเภทการส่งคืนเชิงตรรกะ

อย่างที่คุณเห็นในขณะที่Derefเป็นกรณีการใช้งานที่ชัดเจน (ข้อ จำกัด ทางเทคนิค) แต่กรณีของการAddตัดที่ชัดเจนน้อยกว่า: อาจจะสมเหตุสมผลที่i32 + i32จะให้ผลอย่างใดอย่างหนึ่งi32หรือComplex<i32>ขึ้นอยู่กับบริบท? อย่างไรก็ตามผู้เขียนใช้ดุลยพินิจของพวกเขาและตัดสินใจว่าการเพิ่มประเภทการคืนสินค้ามากเกินไปนั้นไม่จำเป็น

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


4
ให้ฉันพยายามที่จะลดความซับซ้อนของบิต: trait/struct MyTrait/MyStructช่วยให้ตรงหนึ่งหรือ impl MyTrait for อนุญาตหลายs เพราะเป็นเรื่องทั่วไป สามารถเป็นประเภทใดก็ได้ โครงสร้างทั่วไปเหมือนกัน impl MyStructtrait MyTrait<Return>implReturn
Paul-Sebastian Manole

2
ฉันพบว่าคำตอบของคุณเข้าใจง่ายกว่าคำตอบใน "The Rust Programming Language"
drojf

"อันแรกบังคับว่าสำหรับตัวเองมีการส่งคืนเดียวที่เกี่ยวข้อง" นี่เป็นความจริงในความหมายทันที แต่แน่นอนว่าเราสามารถแก้ไขข้อ จำกัด นี้ได้โดยการแบ่งคลาสย่อยด้วยลักษณะทั่วไป บางที Unicity อาจเป็นเพียงคำแนะนำและไม่ได้บังคับใช้
โจเอล

38

ประเภทที่เกี่ยวข้องเป็นกลไกการจัดกลุ่มดังนั้นจึงควรใช้เมื่อควรจัดกลุ่มประเภทเข้าด้วยกัน

Graphลักษณะแนะนำในเอกสารเป็นเช่นนี้ คุณต้องการGraphให้เป็นแบบทั่วไป แต่เมื่อคุณมีไฟล์Graphแล้วคุณไม่ต้องการให้NodeหรือEdgeประเภทแตกต่างกันอีกต่อไป โดยเฉพาะGraphจะไม่ต้องการเปลี่ยนแปลงประเภทเหล่านั้นภายในการใช้งานครั้งเดียวและในความเป็นจริงต้องการให้เหมือนกันเสมอ พวกเขากำลังรวมกลุ่มกันหรือหนึ่งอาจจะบอกว่าที่เกี่ยวข้อง


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