Implementasi sifat yang bertentangan di Rust

Saya ingin menerapkan sifat khusus untuk &'a str dan untuk bilangan bulat hingga i32, tetapi Rust tidak mengizinkan saya untuk:

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() {}

Ini gagal dikompilasi dengan kesalahan berikut:

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 tidak mengimplementasikan Into<i32>. Apakah mungkin menerapkan UiId untuk &'a str dan segala sesuatu yang dapat diubah menjadi i32 tanpa menentukan tipe konkret? Bagaimana saya bisa melakukan itu?


person Slava Baginov    schedule 26.08.2016    source sumber
comment
Saya pikir ini pasti menjadi batasan dalam cara Rust menentukan apakah implementasinya tumpang tindih (atau mungkin tumpang tindih), tetapi saya belum menemukan aturan yang dijelaskan di mana pun. :-( Oh, untuk spesifikasi bahasa yang tepat!   -  person Chris Emerson    schedule 26.08.2016


Jawaban (2)


Fakta bahwa &'a str tidak mengimplementasikan Into<i32> tidak diperhitungkan, karena tidak ada jaminan bahwa &'a str tidak dapat ditambahkan nanti. Ini kemudian akan merusak kode Anda.

Jadi, jika hal ini dibiarkan, kemungkinan kerusakan akan mempersulit penambahan implementasi pada ciri-ciri perpustakaan.

Sayangnya saya tidak dapat menemukan dokumentasi untuk itu, baik di Buku Bahasa Pemrograman Rust maupun di Panduan Referensi.

Yang terbaik yang bisa saya temukan adalah RFC 1023, yang menyatakan bahwa sebuah peti [...] tidak dapat diandalkan oleh Type: !Trait kecuali Type atau Trait adalah lokal.

person starblue    schedule 26.08.2016
comment
Sebenarnya menurutku itu tidak benar. Bukankah ini gunanya peraturan anak yatim piatu? Anda tidak dapat menambahkan sifat asing impls untuk tipe asing. Setidaknya satu sifat dan tipe harus didefinisikan dalam peti saat ini... agar kompiler dapat beralasan tentang kasus seperti ini, Saya pikir. - person Lukas Kalbertodt; 26.08.2016
comment
@LukasKalbertodt Lihat hasil edit saya. Kalimatnya sama dengan aturan ophan di RFC 1023. - person starblue; 26.08.2016
comment
Oh ya, kedengarannya masuk akal. Terima kasih! - person Lukas Kalbertodt; 26.08.2016

Saya menemukan solusi menggunakan sifat penanda. Tidak diperlukan fitur malam atau eksperimental. Caranya adalah saya mendefinisikan sifat penanda di peti saya dan tidak mengekspornya, sehingga tidak mungkin peti hulu menentukan penanda pada kelas selain yang saya terapkan.

Di bawah sifat penanda adalah Numeric.

Saya menggunakan ini sehingga saya dapat mengimplementasikan Into untuk apa pun yang dapat diubah menjadi f64, tetapi juga untuk string dalam impl terpisah, dan tipe lainnya juga.

Sifat Numeric haruslah pub karena merupakan peringatan bahwa versi mendatang akan melarang Sifat pribadi di antarmuka publik.


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