Pertanyaan Bagaimana cara Rust menerapkan refleksi?


Karat memiliki Any sifat, tetapi juga memiliki "tidak membayar untuk apa yang Anda tidak menggunakan" kebijakan. Bagaimana cara Rust menerapkan refleksi?

Dugaan saya adalah bahwa Rust menggunakan pemberian tag malas. Setiap jenis pada awalnya tidak ditugaskan, tetapi kemudian jika suatu instance dari jenis tersebut diteruskan ke fungsi yang mengharapkan suatu Any trait, tipe yang diberikan a TypeId.

Atau mungkin Rust menaruhnya TypeId pada setiap jenis yang contohnya mungkin dilewatkan ke fungsi itu? Saya kira yang pertama akan mahal.


32
2018-04-05 03:46


asal


Jawaban:


Pertama-tama, Rust tidak memiliki refleksi; refleksi menyiratkan bahwa Anda dapat memperoleh detail tentang suatu jenis saat runtime, seperti bidang, metode, antarmuka yang diimplementasikan, dll.  Kamu tidak bisa lakukan ini dengan Rust. Yang paling dekat Anda bisa dapatkan adalah secara eksplisit menerapkan (atau menurunkan) suatu sifat yang menyediakan informasi ini.

Setiap jenis mendapat TypeId ditugaskan untuk itu pada waktu kompilasi. Karena memiliki ID pesanan global keras, ID adalah integer yang berasal dari kombinasi dari definisi jenis, dan berbagai macam metadata tentang peti yang berisi itu. Dengan kata lain: mereka tidak ditugaskan dalam urutan apa pun, hanya saja hash dari berbagai bit informasi yang masuk ke mendefinisikan jenis. [1]

Jika Anda melihat sumber untuk Any sifat, Anda akan melihat penerapan tunggal untuk Any:

impl<T: 'static + ?Sized > Any for T {
    fn get_type_id(&self) -> TypeId { TypeId::of::<T>() }
}

(Batasannya bisa secara informal direduksi menjadi "semua jenis yang tidak dipinjam dari sesuatu yang lain".)

Anda juga dapat menemukan definisi TypeId:

pub struct TypeId {
    t: u64,
}

impl TypeId {
    pub const fn of<T: ?Sized + 'static>() -> TypeId {
        TypeId {
            t: unsafe { intrinsics::type_id::<T>() },
        }
    }
}

intrinsics::type_id adalah fungsi internal yang dikenali oleh kompilator yang, diberi tipe, mengembalikan ID jenis internalnya. Panggilan ini hanya akan diganti pada waktu kompilasi dengan ID tipe integer literal; tidak ada sebenarnya telepon di sini. [2] Begitulah caranya TypeId tahu apa ID tipe itu. TypeId, kemudian, hanya pembungkus di sekitar ini u64 untuk menyembunyikan detail penerapan dari pengguna. Jika Anda merasa lebih mudah secara konseptual, Anda bisa memikirkan jenisnya TypeId sebagai integer 64-bit konstan yang hanya kompilator tahu pada waktu kompilasi.

Any maju ke ini dari get_type_id, yang berarti bahwa get_type_id aku s sangat hanya mengikat metode sifat ke yang sesuai TypeId::of metode. Hanya ada untuk memastikan bahwa jika Anda memiliki Any, Anda dapat mengetahui tipe aslinya TypeId.

Sekarang, Any diimplementasikan untuk paling jenis, tetapi ini tidak berarti bahwa semua jenis itu sebenarnya punya sebuah Any implementasi mengambang di dalam memori. Apa yang sebenarnya terjadi adalah kompilator hanya menghasilkan kode aktual untuk tipe itu Any implementasi jika some one menulis kode yang membutuhkannya. [3] Dengan kata lain, jika Anda tidak pernah menggunakan Any implementasi untuk tipe yang diberikan, kompilator tidak akan pernah menghasilkannya.

Ini adalah bagaimana Rust memenuhi "tidak membayar untuk apa yang tidak Anda gunakan": jika Anda tidak pernah melewati tipe yang diberikan sebagai &Anyatau Box<Any>, maka kode yang terkait tidak pernah dibuat dan tidak pernah menghabiskan ruang apa pun dalam biner Anda yang dikompilasi.


[1]: Frustrasi, ini berarti bahwa itu adalah tipe TypeId bisa mengubah nilai tergantung tepatnya bagaimana perpustakaan akan dikompilasi, sampai pada titik yang menyusunnya sebagai ketergantungan (bukan sebagai sebab yang berdiri sendiri) TypeIds untuk berubah.

[2]: Sejauh yang saya tahu. saya bisa salah tentang ini, tapi aku akan sangat terkejut jika itu yang terjadi.

[3]: Ini umumnya kebenaran generik di Rust.


43
2018-04-05 04:09