การใช้งานคุณลักษณะที่ขัดแย้งกันใน Rust

ฉันต้องการใช้คุณสมบัติที่กำหนดเองสำหรับ &'a str และสำหรับจำนวนเต็มจนถึง i32 แต่ Rust ไม่อนุญาตให้ฉัน:

use std::convert::Into;

pub trait UiId {
    fn push(&self);
}

impl<'a> UiId for &'a str {
    fn push(&self) {}
}

impl<T: Into<i32>> UiId for T {
    fn push(&self) {}
}

fn main() {}

สิ่งนี้ล้มเหลวในการคอมไพล์ด้วยข้อผิดพลาดต่อไปนี้:

error[E0119]: conflicting implementations of trait `UiId` for type `&str`:
  --> src/main.rs:11:1
   |
7  | impl<'a> UiId for &'a str {
   | ------------------------- first implementation here
...
11 | impl<T: Into<i32>> UiId for T {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `&str`
   |
   = note: upstream crates may add new impl of trait `std::convert::From<&str>` for type `i32` in future versions

&'a str ไม่ได้ใช้ Into<i32> เป็นไปได้ไหมที่จะใช้ UiId สำหรับ &'a str และทุกสิ่งที่สามารถแปลงเป็น i32 โดยไม่ต้องระบุประเภทที่เป็นรูปธรรม ฉันจะทำเช่นนั้นได้อย่างไร?


person Slava Baginov    schedule 26.08.2016    source แหล่งที่มา
comment
ฉันคิดว่ามันจะต้องมีข้อ จำกัด ในวิธีที่ Rust พิจารณาว่าการใช้งานซ้อนทับ (หรืออาจทับซ้อนกัน) แต่ฉันไม่พบที่ใดที่มีการสะกดกฎ :-( โอ้สำหรับสเป็คภาษาที่เหมาะสม!   -  person Chris Emerson    schedule 26.08.2016


คำตอบ (2)


ความจริงที่ว่า &'a str ไม่ได้ใช้ Into<i32> จะไม่ถูกนำมาพิจารณา เนื่องจากไม่มีการรับประกันว่าจะไม่สามารถเพิ่มได้ในภายหลัง นี่จะทำให้รหัสของคุณเสียหาย

ดังนั้นหากได้รับอนุญาต การแตกหักที่เป็นไปได้จะทำให้การเพิ่มการใช้งานให้กับคุณลักษณะของไลบรารีทำได้ยากขึ้น

น่าเสียดายที่ฉันไม่พบเอกสารสำหรับเรื่องนั้น ทั้งใน The Rust Programming Language Book หรือใน คู่มืออ้างอิง

สิ่งที่ดีที่สุดที่ฉันหาได้คือ RFC 1023 ซึ่งบอกว่า ลัง [...] ไม่สามารถเชื่อได้ว่า Type: !Trait ถืออยู่ เว้นแต่ Type หรือ Trait จะอยู่ในพื้นที่

person starblue    schedule 26.08.2016
comment
จริงๆแล้วฉันไม่คิดว่ามันเป็นเรื่องจริง กฎของเด็กกำพร้ามีไว้เพื่อสิ่งนี้ไม่ใช่หรือ? คุณไม่สามารถเพิ่มลักษณะต่างประเทศ impls สำหรับประเภทต่างประเทศได้ ต้องมีการกำหนดลักษณะและประเภทอย่างน้อยหนึ่งรายการในกล่องปัจจุบัน... เพื่อให้คอมไพเลอร์ให้เหตุผลเกี่ยวกับกรณีเช่นนี้ ฉันคิดว่า - person Lukas Kalbertodt; 26.08.2016
comment
@LukasKalbertodt ดูการแก้ไขของฉัน อยู่ในประโยคเดียวกับกฎโอแฟนใน RFC 1023 - person starblue; 26.08.2016
comment
โอ้ ฟังดูสมเหตุสมผลจริงๆ ขอบคุณ! - person Lukas Kalbertodt; 26.08.2016

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

ด้านล่างลักษณะเครื่องหมายคือ Numeric

ฉันใช้สิ่งนี้เพื่อที่ฉันจะสามารถนำไปใช้ Into กับทุกสิ่งที่สามารถแปลงเป็น f64 ได้ แต่ยังใช้กับสตริงใน impl แยกต่างหากและประเภทอื่น ๆ เช่นกัน

คุณลักษณะ Numeric ต้องเป็น pub เนื่องจากเป็นการเตือนว่าเวอร์ชันในอนาคตจะไม่อนุญาตให้ใช้คุณลักษณะส่วนตัวในอินเทอร์เฟซสาธารณะ


use std::convert::Into;

pub trait Numeric {}
impl Numeric for f64 {}
impl Numeric for f32 {}
impl Numeric for i64 {}
impl Numeric for i32 {}
impl Numeric for i16 {}
impl Numeric for i8 {}
impl Numeric for isize {}
impl Numeric for u64 {}
impl Numeric for u32 {}
impl Numeric for u16 {}
impl Numeric for u8 {}
impl Numeric for usize {}


pub trait UiId {
    fn push(&self);
}

impl<'a> UiId for &'a str {
    fn push(&self) {}
}

impl<T: Into<i32> + Numeric> UiId for T {
    fn push(&self) {}
}

person Paul Chernoch    schedule 08.03.2021