Apa itu "tipe mendasar" di Rust?

37

Di suatu tempat saya mengambil istilah "tipe fundamental" (dan atributnya #[fundamental]) dan barusan saya ingin belajar lebih banyak tentangnya. Samar-samar saya ingat itu tentang relaksasi aturan koherensi dalam beberapa situasi. Dan saya pikir tipe referensi adalah tipe fundamental.

Sayangnya, mencari di web tidak membawa saya terlalu jauh. Referensi Rust tidak menyebutkannya (sejauh yang saya bisa lihat). Saya baru saja menemukan masalah tentang membuat tuple tipe fundamental dan RFC yang memperkenalkan atribut . Namun, RFC memiliki satu paragraf tentang tipe dasar:

  • Sebuah #[fundamental]jenis Fooadalah salah satu di mana menerapkan impl selimut lebih Fooadalah perubahan melanggar. Seperti yang dijelaskan, &dan &mutbersifat mendasar. Atribut ini akan diterapkan Box, membuat Box berperilaku sama dengan &dan &mutsehubungan dengan koherensi.

Saya menemukan kata-kata yang cukup sulit untuk dipahami dan rasanya seperti saya membutuhkan pengetahuan mendalam tentang RFC lengkap untuk memahami sedikit tentang tipe-tipe fundamental ini. Saya berharap seseorang dapat menjelaskan tipe-tipe fundamental dalam istilah yang lebih sederhana (tanpa menyederhanakan terlalu banyak, tentu saja). Pertanyaan ini juga berfungsi sebagai bagian pengetahuan yang mudah ditemukan.

Untuk memahami tipe-tipe fundamental, saya ingin menjawab pertanyaan-pertanyaan ini (selain pertanyaan utama "apa saja mereka?", Tentu saja):

  • Bisakah tipe fundamental melakukan lebih dari yang non fundamental?
  • Bisakah saya, sebagai penulis perpustakaan, mendapatkan manfaat dengan menandai beberapa tipe saya sebagai #[fundamental]?
  • Jenis mana dari bahasa inti atau perpustakaan standar yang mendasar?
Lukas Kalbertodt
sumber

Jawaban:

34

Biasanya, jika perpustakaan memiliki tipe generik Foo<T>, peti hilir tidak dapat menerapkan sifat itu, bahkan jikaT adalah tipe lokal. Sebagai contoh,

( crate_a)

struct Foo<T>(pub t: T)

( crate_b)

use crate_a::Foo;

struct Bar;

// This causes an error
impl Clone for Foo<Bar> {
    fn clone(&self) -> Self {
        Foo(Bar)
    }
}

Untuk contoh nyata yang bekerja di taman bermain (yaitu, memberikan kesalahan),

use std::rc::Rc;

struct Bar;

// This causes an error
// error[E0117]: only traits defined in the current crate
// can be implemented for arbitrary types
impl Default for Rc<Bar> {
    fn default() -> Self {
        Rc::new(Bar)
    }
}

(tempat bermain)


Ini biasanya memungkinkan pembuat peti untuk menambahkan (menyelimuti) implementasi sifat-sifat tanpa melanggar peti hilir. Itu bagus dalam kasus-kasus di mana awalnya tidak pasti bahwa suatu tipe harus menerapkan sifat tertentu, tetapi kemudian menjadi jelas bahwa itu harus. Sebagai contoh, kita mungkin memiliki semacam tipe numerik yang awalnya tidak menerapkan sifat-sifat dari num-traits. Ciri-ciri itu dapat ditambahkan kemudian tanpa perlu perubahan.

Namun, dalam beberapa kasus, penulis perpustakaan menginginkan peti hilir agar dapat menerapkan sifat-sifat itu sendiri. Di sinilah #[fundamental]atribut masuk. Ketika ditempatkan pada suatu jenis, sifat apa pun yang saat ini tidak diterapkan untuk jenis itu tidak akan diterapkan (kecuali perubahan yang melanggar). Akibatnya, peti hilir dapat menerapkan sifat-sifat untuk tipe tersebut selama parameter tipe bersifat lokal (ada beberapa aturan rumit untuk memutuskan parameter tipe mana yang menghitung ini). Karena tipe fundamental tidak akan menerapkan sifat yang diberikan, sifat itu dapat secara bebas diimplementasikan tanpa menyebabkan masalah koherensi.

Misalnya, Box<T>ditandai #[fundamental], jadi kode berikut (mirip dengan Rc<T>versi di atas) berfungsi. Box<T>tidak menerapkan Default(kecuali Tmengimplementasikan Default) sehingga kami dapat berasumsi bahwa itu tidak akan terjadi di masa depan karena Box<T>bersifat mendasar. Perhatikan bahwa menerapkan Defaultuntuk Barakan menyebabkan masalah, sejak itu Box<Bar>sudah mengimplementasikan Default.

struct Bar;

impl Default for Box<Bar> {
    fn default() -> Self {
        Box::new(Bar)
    }
}

(tempat bermain)


Di sisi lain, ciri-ciri juga dapat ditandai dengan #[fundamental]. Ini memiliki makna ganda untuk tipe fundamental. Jika jenis apa pun saat ini tidak menerapkan sifat mendasar, dapat diasumsikan bahwa jenis itu tidak akan menerapkannya di masa mendatang (sekali lagi, kecuali perubahan yang melanggar). Saya tidak begitu yakin bagaimana ini digunakan dalam praktek. Dalam kode (tertaut di bawah), FnMutditandai mendasar dengan catatan bahwa itu diperlukan untuk regex (sesuatu tentang &str: !FnMut). Saya tidak dapat menemukan di mana itu digunakan dalam regexpeti atau jika digunakan di tempat lain.

Secara teori, jika Addsifat itu ditandai fundamental (yang telah dibahas) ini dapat digunakan untuk mengimplementasikan penambahan antara hal-hal yang belum memilikinya. Misalnya, menambahkan [MyNumericType; 3](menunjuk), yang dapat berguna dalam situasi tertentu (tentu saja, membuat [T; N]fundamental juga akan memungkinkan ini).


Jenis mendasar primitif yang &T, &mut T(lihat di sini untuk demonstrasi semua tipe primitif generik). Di perpustakaan standar, Box<T>dan Pin<T>juga ditandai sebagai fundamental.

Ciri-ciri mendasar di perpustakaan standar yang Sized, Fn<T>, FnMut<T>, FnOnce<T>dan Generator.


Perhatikan bahwa #[fundamental]atribut saat ini tidak stabil. Masalah pelacakan adalah masalah # 29635 .

SCappella
sumber
1
Jawaban bagus! Mengenai tipe primitif: hanya ada segelintir generik tipe primitif: &T, &mut T, *const T, *mut T, [T; N], [T], fnpointer dan tupel. Dan menguji semuanya (tolong katakan padaku jika kode ini tidak masuk akal) tampaknya referensi adalah satu-satunya tipe primitif mendasar . Menarik. Saya akan tertarik mengetahui alasan mengapa yang lain tidak, terutama petunjuk mentah. Tapi itu bukan ruang lingkup pertanyaan ini, saya kira.
Lukas Kalbertodt
1
@LukasKalbertodt Terima kasih atas informasi tentang tipe primitif. Saya telah menambahkan dalam tes Anda. Adapun alasan tentang referensi vs pointer, lihat komentar ini di permintaan tarik RFC.
SCappella
Referensi tidak mendokumentasikan atribut yang tidak stabil, jadi itu sebabnya Anda tidak menemukannya di sana.
Havvy