Tim pengembangan Rust dengan bangga mengumumkan rilis versi baru Rust: 1.27.0. Rust adalah bahasa pemrograman sistem yang ditujukan untuk keamanan, kecepatan, dan eksekusi kode paralel.
Jika Anda memiliki versi Rust sebelumnya yang diinstal menggunakan rustup, maka untuk meningkatkan versi Rust ke versi 1.27.0 Anda hanya perlu melakukan:
$ rustup update stable
Jika Anda belum menginstal rustup, Anda dapat menginstalnya dari halaman yang sesuai di situs web kami. Catatan rilis terperinci untuk Rust 1.27.0 tersedia di GitHub.
Kami juga ingin menarik perhatian Anda pada hal ini: sebelum rilis versi 1.27.0, kami menemukan kesalahan dalam pemetaan match
diperkenalkan di versi 1.26.0, yang dapat menyebabkan perilaku tidak benar. Karena ditemukan sangat terlambat, sudah dalam proses merilis versi ini, meskipun sudah ada sejak versi 1.26.0, kami memutuskan untuk tidak menghentikan rutinitas dan menyiapkan versi tetap 1.27.1, yang akan dirilis dalam waktu dekat. Dan tambahan, jika perlu, versi 1.26.3. Detail dapat ditemukan di catatan rilis yang sesuai.
Apa yang termasuk dalam versi stabil 1.27.0
Dalam masalah ini, dua peningkatan bahasa yang besar dan lama ditunggu-tunggu muncul. Tapi pertama-tama, komentar kecil pada dokumentasi: pencarian sekarang tersedia di semua buku di perpustakaan Rust ! Misalnya, Anda dapat menemukan "pinjam" di buku "Bahasa Pemrograman Karat" . Kami harap ini memudahkan Anda menemukan informasi yang Anda butuhkan. Selain itu, Buku baru tentang rustc telah muncul . Buku ini menjelaskan cara menggunakan rustc
secara langsung, dan cara mendapatkan informasi bermanfaat lainnya, seperti daftar semua pemeriksaan statis.
SIMD
Jadi, sekarang yang penting: mulai sekarang, fitur dasar menggunakan SIMD tersedia di Rust! SIMD berarti "instruksi tunggal, banyak aliran data" (instruksi tunggal, banyak data). Pertimbangkan fungsinya:
pub fn foo(a: &[u8], b: &[u8], c: &mut [u8]) { for ((a, b), c) in a.iter().zip(b).zip(c) { *c = *a + *b; } }
Di sini kita mengambil dua irisan integer, menjumlahkan elemen mereka dan menempatkan hasilnya di irisan ketiga. Kode di atas menunjukkan cara termudah untuk melakukan ini: Anda harus melalui seluruh rangkaian elemen, menyatukannya dan menyimpan hasilnya. Namun, kompiler sering menemukan solusi yang lebih baik. LLVM sering "secara otomatis membuat vektor" kode yang sama, di mana kata rumit seperti itu berarti "menggunakan SIMD." Bayangkan irisan a
dan b
terdiri dari 16 elemen, keduanya. Setiap elemen adalah u8
, yang berarti irisan akan berisi 128 bit data masing-masing. Dengan menggunakan SIMD, kita dapat menempatkan irisan a
dan b
dalam register 128-bit, menambahkannya bersama dengan satu instruksi, dan kemudian menyalin 128 bit yang dihasilkan ke c
. Ini akan bekerja lebih cepat!
Terlepas dari kenyataan bahwa versi stabil Rust selalu dapat mengambil keuntungan dari vektorisasi otomatis, kadang-kadang kompiler tidak cukup pintar untuk memahami bahwa itu dapat diterapkan dalam kasus ini. Selain itu, tidak semua CPU mendukung fitur ini. Oleh karena itu, LLVM tidak selalu dapat menggunakannya, karena program Anda dapat berjalan di berbagai platform perangkat keras. Oleh karena itu, dalam Rust 1.27, dengan penambahan modul std::arch
, menjadi mungkin untuk menggunakan instruksi semacam ini secara langsung , yaitu, sekarang kita tidak berkewajiban untuk hanya mengandalkan kompilasi cerdas. Selain itu, kami memiliki kesempatan untuk memilih implementasi tertentu, tergantung pada berbagai kriteria. Sebagai contoh:
#[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "avx2"))] fn foo() { #[cfg(target_arch = "x86")] use std::arch::x86::_mm256_add_epi64; #[cfg(target_arch = "x86_64")] use std::arch::x86_64::_mm256_add_epi64; unsafe { _mm256_add_epi64(...); } }
Di sini kita menggunakan flag cfg
untuk memilih versi kode yang benar tergantung pada platform target: pada x86
versinya sendiri akan digunakan, dan pada x86_64
sendiri. Kami juga dapat memilih saat runtime:
fn foo() { #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] { if is_x86_feature_detected!("avx2") { return unsafe { foo_avx2() }; } } foo_fallback(); }
Di sini kami memiliki dua versi fungsi: satu menggunakan AVX2
- jenis SIMD tertentu yang memungkinkan Anda untuk melakukan operasi 256-bit. Makro is_x86_feature_detected!
akan menghasilkan kode yang memeriksa apakah prosesor mendukung AVX2, dan jika demikian, fungsi foo_avx2
akan dipanggil. Jika tidak, kami akan menggunakan implementasi tanpa AVX, foo_fallback
. Jadi kode kami akan bekerja sangat cepat pada prosesor yang mendukung AVX2, tetapi juga akan bekerja pada prosesor lain, meskipun lebih lambat.
Semuanya terlihat agak rendah dan tidak nyaman - ya, benar! std::arch
adalah primitif untuk hal semacam ini. Kami berharap bahwa di masa depan kami akan tetap menstabilkan std::simd
dengan kemampuan tingkat tinggi. Tetapi kemunculan kemampuan SIMD dasar sekarang memungkinkan Anda untuk bereksperimen dengan dukungan tingkat tinggi untuk berbagai perpustakaan. Misalnya, lihat paket yang lebih cepat . Berikut cuplikan kode tanpa SIMD:
let lots_of_3s = (&[-123.456f32; 128][..]).iter() .map(|v| { 9.0 * v.abs().sqrt().sqrt().recip().ceil().sqrt() - 4.0 - 2.0 }) .collect::<Vec<f32>>();
Untuk menggunakan SIMD dalam kode ini dengan faster
, Anda perlu mengubahnya seperti ini:
let lots_of_3s = (&[-123.456f32; 128][..]).simd_iter() .simd_map(f32s(0.0), |v| { f32s(9.0) * v.abs().sqrt().rsqrt().ceil().sqrt() - f32s(4.0) - f32s(2.0) }) .scalar_collect();
Itu terlihat hampir sama: simd_iter
bukan iter
, simd_map
bukan map
, f32s(2.0)
bukannya 2.0
. Tetapi pada akhirnya, Anda mendapatkan versi kode SIMD Anda yang bersertifikat.
Selain itu , Anda tidak akan pernah bisa menulis ini sendiri, tetapi, seperti biasa, perpustakaan tempat Anda bergantung dapat melakukannya. Misalnya, dukungan telah ditambahkan regex
, dan versi barunya akan memiliki akselerasi SIMD tanpa Anda perlu melakukan apa pun!
dyn Trait
Pada akhirnya, kami menyesali sintaksis objek objek yang awalnya dipilih di Rust. Seperti yang Anda ingat, untuk Foo
Anda dapat mendefinisikan objek sifat seperti ini:
Box<Foo>
Namun, jika Foo
adalah struktur, itu hanya berarti menempatkan struktur di dalam Box<T>
. Ketika mengembangkan bahasa, kami berpikir bahwa kesamaan seperti itu akan menjadi ide yang baik, tetapi pengalaman menunjukkan bahwa ini mengarah pada kebingungan. Dan ini bukan hanya Box<Trait>
: impl SomeTrait for SomeOtherTrait
juga sintaks yang benar secara formal, tetapi Anda hampir selalu perlu menulis impl<T> SomeTrait for T where T: SomeOtherTrait
sebagai gantinya. Ini sama dengan impl SomeTrait
, yang terlihat seperti menambahkan metode atau kemungkinan implementasi standar untuk tipe tersebut, tetapi sebenarnya ia menambahkan metode sendiri ke objek tipe. Akhirnya, dibandingkan dengan sintaks impl Trait
baru ditambahkan, sintaks Trait
terlihat lebih pendek dan lebih disukai untuk digunakan, tetapi pada kenyataannya ini tidak selalu benar.
Oleh karena itu, di Rust 1.27, kami menstabilkan sintaks dyn Trait
baru. Objek sifat sekarang terlihat seperti ini:
Demikian pula untuk jenis pointer lainnya: Arc<Foo>
sekarang Arc<dyn Foo>
, dll. Karena persyaratan kompatibilitas ke belakang, kami tidak dapat menghapus sintaks lama, tetapi kami telah menambahkan pemeriksaan statis bare-trait-object
, yang secara default menyelesaikan sintaks lama. Jika Anda ingin melarangnya, maka Anda dapat mengaktifkan pemeriksaan ini. Kami berpikir bahwa dengan cek dihidupkan secara default, sekarang terlalu banyak peringatan akan ditampilkan.
Omong-omong, kami sedang mengerjakan alat yang disebut rustfix
, yang dapat secara otomatis memperbarui kode Anda ke idiom yang lebih baru. Dia akan menggunakan pemeriksaan statis serupa untuk ini. Nantikan pengumuman rustfix
di pengumuman mendatang.
#[must_use]
untuk fungsi
Kesimpulannya, efek dari atribut #[must_use]
diperluas: sekarang dapat digunakan untuk fungsi .
Sebelumnya, ini hanya berlaku untuk jenis, seperti Result <T, E>
. Tetapi sekarang Anda dapat melakukan ini:
#[must_use] fn double(x: i32) -> i32 { 2 * x } fn main() { double(4);
Dengan atribut ini, kami juga sedikit meningkatkan pustaka standar : Clone::clone
, Iterator::collect
dan ToOwned::to_owned
akan memberikan peringatan jika Anda tidak menggunakan nilai pengembaliannya, yang akan membantu Anda melihat operasi mahal yang hasilnya Anda abaikan secara tidak sengaja.
Lihat catatan rilis untuk lebih jelasnya.
Stabilisasi perpustakaan
API baru berikut telah distabilkan dalam rilis ini:
Lihat catatan rilis untuk lebih jelasnya.
Peningkatan Kargo
Cargo telah menerima dua peningkatan kecil dalam rilis ini. Pertama, --target-dir
, yang dapat digunakan untuk mengubah direktori eksekusi target.
Selain itu, pendekatan Cargo untuk bagaimana menangani target telah diselesaikan. Cargo mencoba mendeteksi tes, contoh, dan yang dapat dieksekusi dalam proyek Anda. Namun, konfigurasi eksplisit terkadang diperlukan. Tetapi dalam implementasi awal, ini bermasalah. Katakanlah Anda memiliki dua contoh, dan Cargo mendeteksi keduanya. Anda ingin mengonfigurasi salah satunya, yang Anda tambahkan [[example]]
ke Cargo.toml
untuk menentukan parameter contoh. Saat ini, Cargo akan melihat bahwa Anda telah mendefinisikan contoh secara eksplisit, dan karenanya tidak akan mencoba mendeteksi orang lain secara otomatis. Ini agak menjengkelkan.
Karenanya, kami 'auto'- Cargo.toml
. Kami tidak dapat memperbaiki perilaku ini tanpa kemungkinan gangguan proyek yang mengandalkannya secara tidak sengaja. Oleh karena itu, jika Anda ingin mengonfigurasi beberapa sasaran, tetapi tidak semua, Anda dapat mengatur kunci autoexamples
menjadi true
di bagian [package]
.
Lihat catatan rilis untuk lebih jelasnya.
Pengembang 1.27.0
Banyak orang berpartisipasi dalam pengembangan Rust 1.27. Kami tidak dapat menyelesaikan pekerjaan tanpa Anda masing-masing.
Terima kasih
Dari seorang penerjemah: Saya mengucapkan terima kasih kepada anggota komunitas ruRust dan secara pribadi ozkriff atas bantuan mereka dengan terjemahan dan proofreading