Rust 1.27 Release

Das Rust-Entwicklungsteam freut sich, die Veröffentlichung einer neuen Version von Rust: 1.27.0 bekannt zu geben. Rust ist eine Systemprogrammiersprache, die auf Sicherheit, Geschwindigkeit und parallele Codeausführung abzielt.


Wenn Sie eine frühere Version von Rust mit rustup installiert haben, müssen Sie nur Folgendes tun, um Rust auf Version 1.27.0 zu aktualisieren:


$ rustup update stable 

Wenn Sie rustup noch nicht installiert haben, können Sie es von der entsprechenden Seite unserer Website installieren . Detaillierte Versionshinweise für Rust 1.27.0 sind auf GitHub verfügbar.


Wir möchten Sie auch darauf aufmerksam machen: Vor der Veröffentlichung von Version 1.27.0 haben wir einen Fehler bei der Verbesserung der in Version 1.26.0 eingeführten Übereinstimmungszuordnungen festgestellt, der zu falschem Verhalten führen kann. Da es sehr spät entdeckt wurde, bereits während der Veröffentlichung dieser Version, obwohl es seit Version 1.26.0 vorhanden ist, haben wir beschlossen, die Routine nicht zu unterbrechen und eine feste Version 1.27.1 vorzubereiten, die in naher Zukunft veröffentlicht wird. Und bei Bedarf zusätzlich Version 1.26.3. Details finden Sie in den entsprechenden Versionshinweisen.


Was ist in der stabilen Version 1.27.0 enthalten


In dieser Ausgabe werden zwei große und lang erwartete Sprachverbesserungen vorgestellt. Aber zuerst ein kleiner Kommentar zur Dokumentation: Die Suche ist jetzt in allen Büchern der Rust-Bibliothek verfügbar ! Zum Beispiel finden Sie "Ausleihen" im Buch "Rust Programming Language" . Wir hoffen, dass dies das Auffinden der benötigten Informationen erleichtert. Außerdem ist ein neues Buch über Rustc erschienen . In diesem Buch wird erläutert, wie Sie rustc direkt verwenden und andere nützliche Informationen rustc , z. B. eine Liste aller statischen Überprüfungen.


SIMD


Nun zum Wichtigen: Von nun an sind die Grundfunktionen der Verwendung von SIMD in Rust verfügbar! SIMD bedeutet "Einzelbefehl, Mehrfachdatenstrom" (Einzelbefehl, Mehrfachdaten). Betrachten Sie die Funktion:


 pub fn foo(a: &[u8], b: &[u8], c: &mut [u8]) { for ((a, b), c) in a.iter().zip(b).zip(c) { *c = *a + *b; } } 

Hier nehmen wir zwei ganzzahlige Schichten, summieren ihre Elemente und setzen das Ergebnis in die dritte Schicht. Der obige Code zeigt den einfachsten Weg, dies zu tun: Sie müssen den gesamten Satz von Elementen durchgehen, sie zusammenfügen und das Ergebnis speichern. Compiler finden jedoch häufig eine bessere Lösung. LLVM "vektorisiert" häufig ähnlichen Code automatisch, wobei eine solch komplizierte Formulierung einfach "verwendet SIMD" bedeutet. Stellen Sie sich vor, die Scheiben a und b sind beide 16 Elemente lang. Jedes Element ist u8 , was bedeutet, dass Slices jeweils 128 Datenbits enthalten. Mit SIMD können wir beide Slices a und b in 128-Bit-Registern platzieren, sie mit einem Befehl addieren und dann die resultierenden 128 Bits nach c kopieren. Es wird viel schneller funktionieren!


Trotz der Tatsache, dass die stabile Version von Rust die automatische Vektorisierung immer nutzen konnte, ist der Compiler manchmal einfach nicht klug genug, um zu verstehen, dass sie in diesem Fall angewendet werden kann. Darüber hinaus unterstützen nicht alle CPUs diese Funktionen. Daher kann LLVM sie nicht immer verwenden, da Ihr Programm auf einer Vielzahl von Hardwareplattformen ausgeführt werden kann. Daher wurde es in Rust 1.27 mit dem Hinzufügen des std::arch Moduls möglich, diese Art von Anweisungen direkt zu verwenden, dh wir sind jetzt nicht verpflichtet, uns nur auf intelligente Kompilierung zu verlassen. Darüber hinaus haben wir die Möglichkeit, abhängig von verschiedenen Kriterien eine bestimmte Implementierung auszuwählen. Zum Beispiel:


 #[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(...); } } 

Hier verwenden wir die cfg Flags, um abhängig von der Zielplattform die richtige Version des Codes auszuwählen: Auf x86 eine eigene Version und auf x86_64 eine eigene Version verwendet. Wir können auch zur Laufzeit wählen:


 fn foo() { #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] { if is_x86_feature_detected!("avx2") { return unsafe { foo_avx2() }; } } foo_fallback(); } 

Hier haben wir zwei Versionen der Funktion: Eine verwendet AVX2 - eine spezielle Art von SIMD, mit der Sie 256-Bit-Operationen ausführen können. Makro is_x86_feature_detected! generiert einen Code, der prüft, ob der Prozessor AVX2 unterstützt. In diesem foo_avx2 wird die Funktion foo_avx2 aufgerufen. Wenn nicht, greifen wir auf eine Implementierung ohne AVX zurück, foo_fallback . Unser Code funktioniert also sehr schnell auf Prozessoren, die AVX2 unterstützen, aber auch auf anderen Prozessoren, wenn auch langsamer.


Es sieht alles ein bisschen leise und unangenehm aus - ja, das ist es! std::arch sind die Grundelemente für solche Dinge. Wir hoffen, dass wir das std::simd in Zukunft mit High-Level-Funktionen stabilisieren können. Mit dem Aufkommen grundlegender SIMD-Funktionen können Sie jetzt mit der Unterstützung verschiedener Bibliotheken auf hoher Ebene experimentieren. Schauen Sie sich zum Beispiel das schnellere Paket an. Hier ist ein Codeausschnitt ohne 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>>(); 

Um SIMD in diesem Code faster , müssen Sie ihn folgendermaßen ändern:


 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(); 

Es sieht fast gleich aus: simd_iter statt iter , simd_map statt map , f32s(2.0) statt 2.0 . Am Ende erhalten Sie jedoch eine SIMD-zertifizierte Version Ihres Codes.


Außerdem können Sie dies niemals selbst schreiben, aber wie immer können die Bibliotheken, auf die Sie angewiesen sind, dies tun. Zum Beispiel wurde regex , und die neue Version wird eine SIMD-Beschleunigung aufweisen, ohne dass Sie überhaupt etwas tun müssen!


dyn Trait


Am Ende haben wir die ursprünglich ausgewählte Syntax von Merkmalsobjekten in Rust bedauert. Wie Sie sich erinnern, können Sie für ein Foo ein Merkmalobjekt wie Foo definieren:


 Box<Foo> 

Wenn Foo jedoch eine Struktur wäre, würde dies einfach bedeuten, die Struktur in eine Box<T> . Bei der Entwicklung der Sprache hielten wir solche Ähnlichkeiten für eine gute Idee, aber die Erfahrung hat gezeigt, dass dies zu Verwirrung führt. Und es ist nicht nur Box<Trait> : impl SomeTrait for SomeOtherTrait auch eine formal korrekte Syntax, sondern Sie müssen fast immer impl<T> SomeTrait for T where T: SomeOtherTrait schreiben, impl<T> SomeTrait for T where T: SomeOtherTrait stattdessen. impl SomeTrait , das dem Typ Methoden oder eine mögliche Standardimplementierung impl SomeTrait , dem impl SomeTrait jedoch eigene Methoden hinzufügt. Im Vergleich zur kürzlich hinzugefügten impl Trait Syntax sieht die Trait Syntax kürzer aus und ist vorzuziehen, aber in Wirklichkeit ist dies nicht immer der Fall.


Daher haben wir in Rust 1.27 die neue dyn Trait Syntax stabilisiert. Merkmalsobjekte sehen jetzt so aus:


 //  =>  Box<Foo> => Box<dyn Foo> &Foo => &dyn Foo &mut Foo => &mut dyn Foo 

Ähnliches gilt für andere Zeigertypen: Arc<Foo> jetzt Arc<dyn Foo> usw. Aufgrund der Abwärtskompatibilitätsanforderungen können wir die alte Syntax nicht entfernen, haben jedoch eine statische Prüfung für bare-trait-object hinzugefügt, die standardmäßig die alte Syntax auflöst. Wenn Sie es verbieten möchten, können Sie diese Prüfung aktivieren. Wir dachten, dass bei standardmäßig aktivierter Prüfung jetzt zu viele Warnungen angezeigt werden.


Übrigens arbeiten wir an einem Tool namens rustfix , mit dem Sie Ihren Code automatisch auf neuere Redewendungen aktualisieren können. Er wird dafür ähnliche statische Prüfungen durchführen. rustfix Sie rustfix auf rustfix Ankündigungen in zukünftigen Ankündigungen.

#[must_use] für Funktionen


Zusammenfassend wurde der Effekt des Attributs #[must_use] erweitert: Jetzt kann es für Funktionen verwendet werden .


Bisher galt es nur für Typen wie Result <T, E> . Aber jetzt können Sie dies tun:


 #[must_use] fn double(x: i32) -> i32 { 2 * x } fn main() { double(4); // warning: unused return value of `double` which must be used let _ = double(4); // (no warning) } 

Mit diesem Attribut haben wir auch die Standardbibliothek leicht verbessert : Clone::clone , Iterator::collect und ToOwned::to_owned geben Warnungen aus, wenn Sie ihre Rückgabewerte nicht verwenden. ToOwned::to_owned können Sie teure Vorgänge feststellen, deren Ergebnisse Sie versehentlich ignorieren.


Weitere Informationen finden Sie in den Versionshinweisen.


Bibliotheksstabilisierung


Die folgenden neuen APIs wurden in dieser Version stabilisiert:



Weitere Informationen finden Sie in den Versionshinweisen.


Frachtverbesserungen


Cargo hat in dieser Version zwei kleinere Verbesserungen erhalten. Zunächst wurde ein --target-dir , mit dem das --target-dir geändert werden kann.


Darüber hinaus wurde der Ansatz von Cargo im Umgang mit Zielen abgeschlossen. Cargo versucht, Tests, Beispiele und ausführbare Dateien in Ihrem Projekt zu erkennen. Manchmal ist jedoch eine explizite Konfiguration erforderlich. Bei der ersten Implementierung war dies jedoch problematisch. Angenommen, Sie haben zwei Beispiele, und Cargo erkennt beide. Sie möchten eine davon konfigurieren, für die Sie Cargo.toml [[example]] Cargo.toml , um Beispielparameter anzugeben. Derzeit sieht Cargo, dass Sie das Beispiel explizit definiert haben, und versucht daher nicht, andere automatisch zu erkennen. Das ist ein bisschen ärgerlich.


Aus diesem 'auto'- Cargo.toml wir 'auto'- Cargo.toml . Wir können dieses Verhalten nicht beheben, ohne dass möglicherweise Projekte aufgeschlüsselt werden, die versehentlich darauf angewiesen sind. Wenn Sie einige, aber nicht alle Ziele konfigurieren möchten, können Sie daher den autoexamples Schlüssel im Abschnitt [package] auf true .


Weitere Informationen finden Sie in den Versionshinweisen.


Entwickler 1.27.0


Viele Leute waren an der Entwicklung von Rust 1.27 beteiligt. Ohne jeden von Ihnen hätten wir die Arbeit nicht abschließen können.


Vielen Dank!


Von einem Übersetzer: Ich danke den Mitgliedern der ruRust-Community und persönlich ozkriff für ihre Hilfe bei der Übersetzung und beim Korrekturlesen

Source: https://habr.com/ru/post/de415091/


All Articles