[Übersetzung] Erste Schritte mit Rust für Python-Programmierer

Dieser Artikel ist eine Übersetzung eines Artikels von Armin Ronacher (@mitsuhiko) am Mittwoch, den 27. Mai 2015.

Da der Übersetzer Rust überhaupt nicht kennt, kann es zu Missverständnissen und Fehlübersetzungen (insbesondere Begriffen) kommen. Wenn Sie einen solchen Fehler finden, wäre es hilfreich, wenn Sie uns eine Bearbeitungsanforderung senden könnten.

Erste Schritte mit Rust für Python-Programmierer

Jetzt, da Rust 1.0 veröffentlicht und sehr stabil ist, hielt ich es für interessant, einen Einführungsartikel über Rust für Python-Programmierer zu schreiben. Dieser Leitfaden untersucht die Grundlagen der Rust-Sprache und vergleicht verschiedene Konstrukte und deren Verhalten.

Die Sprache Rust ist ein ganz anderes Tier als Python. Eine ist nicht nur eine kompilierte Sprache und die andere eine interpretierte Sprache, sie unterscheiden sich auch in ihren wichtigsten Sprachmerkmalen erheblich. Daher sind die Kernteile der Sprache sehr unterschiedlich, aber diese Sprachen haben viel gemeinsam in Bezug auf die Idee, wie die API funktioniert. Als Python-Programmierer sind Ihnen viele der Konzepte sehr vertraut.

Syntax

Der erste Unterschied, den Python-Programmierer bemerken, ist die Syntax. Im Gegensatz zu Python ist Rust eine Sprache mit vielen mittleren Klammern. Dafür gibt es aber gute Gründe. Das liegt daran, dass Rust anonyme Funktionen, Schließungen und viele Verkettungsfunktionen hat, die Python nicht gut unterstützt. In nicht einrückungsbasierten Sprachen erleichtern diese Funktionen das Schreiben und Verstehen von Code erheblich. Schauen wir uns nun dasselbe Beispiel in beiden Sprachen an.

Hier ist zunächst ein Beispiel für Python, das dreimal "Hello World" anzeigt.

    def main():
        for count in range(3):
            print "{}. Hello World!".format(count)

Das gleiche Beispiel in Rust.

    fn main() {
        for count in 0..3 {
            println!("{}. Hello World!", count);
        }
    }

Wie Sie sehen können, sind sie sehr ähnlich. def wird zu fn und der Doppelpunkt wird zu einer mittleren Klammer. Der andere große syntaktische Unterschied besteht darin, dass Rust Typinformationen für Funktionsparameter benötigt. Das machen Sie in Python nicht. Typanmerkungen sind in Python 3 verfügbar, sodass Sie dieselbe Syntax verwenden können, die Sie in Rust finden würden.

Eines der neuen Konzepte im Vergleich zu Python ist die Funktion mit einem Ausrufezeichen am Ende. Sie sind Makros. Makros werden zur Kompilierungszeit erweitert. Dieses Makro wird beispielsweise für das Zeichenfolgenformat und dessen Ausgabe verwendet. Dieses Makro ist eine Möglichkeit, den Compiler zu zwingen, zur Kompilierungszeit die richtige Formatzeichenfolge zu haben. Auf diese Weise machen Sie keinen Fehler in der Anzahl oder Art der Argumente, die an die Ausgabefunktion übergeben werden.

Trate vs. Protokoll

Man kann sagen, dass das Verhalten eines Objekts das bekannteste ist, aber unterschiedliche Eigenschaften aufweist. In Python kann eine Klasse ein bestimmtes Verhalten auswählen, indem sie eine spezielle Methode implementiert. Dies wird üblicherweise als "Befolgen des Protokolls" bezeichnet. Implementieren Sie beispielsweise die Methode iter, die einen Iterator zurückgibt, um ein iterierbares Objekt zu erstellen. Diese Methoden müssen in der Klasse selbst implementiert werden. Und später kann _aktuell _ nicht geändert werden (Monkeypatch ignoriert).

Rusts Konzept ist sehr ähnlich, verwendet jedoch Merkmale anstelle spezieller Methoden. Die Merkmale haben eine etwas andere Art, den gleichen Zweck zu erreichen, aber ihre Implementierung wird in den lokalen Bereich gestellt, und Sie können mehr Merkmale für den Typ aus einem anderen Modul implementieren. Wenn Sie beispielsweise möchten, dass eine Ganzzahl ein spezielles Verhalten aufweist, können Sie dies tun, ohne den Ganzzahltyp zu ändern.

Um dieses Konzept zu vergleichen, schauen wir uns an, wie ein Typ implementiert wird, der dem Typ selbst hinzugefügt wird. Zuerst von Python.

    class MyType(object):
    
        def __init__(self, value):
            self.value = value
    
        def __add__(self, other):
            if not isinstance(other, MyType):
                return NotImplemented
            return self.__class__(self.value + other.value)    

Das gleiche Beispiel in Rust.

    use std::ops::Add;
    
    struct MyType {
        value: i32,
    }
    
    impl MyType {
        fn new(value: i32) -> MyType {
            MyType { value: value }
        }
    }
    
    impl Add for MyType {
        type Output = MyType;
    
        fn add(self, other: MyType) -> MyType {
            MyType { value: self.value + other.value }
        }
    }

Rusts Code ist hier etwas länger, behandelt aber auch automatische Typen, die Python-Code nicht hat. Als erstes fällt auf, dass die Methoden in Python zu Klassen gehören, während in Rust die Daten und ihre Operationen unabhängig sind. struct definiert das Datenlayout. impl MyType definiert die Methoden, über die der Typ selbst verfügt, während impl Add for My Type das Merkmal Add für diesen Typ implementiert. Für die Implementierung von Add müssen wir auch den Ergebnistyp der hier implementierten Add-Operation definieren, aber es beseitigt die zusätzliche Komplexität der Laufzeit-Typprüfung, die Sie in Python durchführen müssten.

Ein weiterer Unterschied ist, dass der Konstruktor in Rust explizit ist, während er in Python ziemlich magisch ist. Wenn Sie in Python eine Instanz eines Objekts erstellen, rufen Sie am Ende "init" auf, um das Objekt zu initialisieren. Rust definiert andererseits einfach eine statische Methode (gemäß der Konvention "neu"), die Objekte zusammensetzt und zuweist.

Fehlerbehandlung

Die Fehlerbehandlung in Python und Rust ist sehr unterschiedlich. Python-Fehler werden als Ausnahmen ausgelöst, während Rust-Fehler als Rückgabewert zurückgegeben werden. Es mag zunächst seltsam erscheinen, aber es ist ein wirklich gutes Konzept. Es ist leicht zu erkennen, welche Art von Fehler zurückgegeben wird, wenn Sie sich eine Funktion ansehen.

Dies bedeutet, dass Funktionen in Rust Ergebnis zurückgeben können. Ergebnis ist ein parametrisierter Typ, der zwei Aspekte hat: Erfolg und Misserfolg. Beispiel: "Ergebnis <i32, MyError>" bedeutet, dass bei Erfolg dieser Funktion eine 32-Bit-Ganzzahl zurückgegeben wird. Wenn ein Fehler auftritt, wird "MyError" zurückgegeben. Was ist, wenn ich einen oder mehrere Fehler zurückgeben muss? Hier ist die philosophische Perspektive anders.

In Python können Funktionen mit beliebigen Fehlern fehlschlagen, und Sie können nichts dagegen tun. Wenn Sie die "Anforderungs" -Bibliothek von Python verwendet haben, um alle Anforderungsausnahmen abzufangen, werden Sie das Wesentliche des Problems verstehen, nachdem Sie frustriert sind, dass SSL-Fehler nicht abgefangen werden. Wenn eine Bibliothek nicht dokumentiert, was sie zurückgibt, kann ein Benutzer dieser Bibliothek nur wenig tun.

Das ist eine ganz andere Situation in Rust. Die Funktionssignatur enthält den zurückgegebenen Fehler. Wenn Sie zwei Fehler zurückgeben müssen, können Sie einen benutzerdefinierten Fehlertyp erstellen, um den internen Fehler in einen besseren zu konvertieren. Angenommen, Sie haben eine HTTP-Bibliothek. Wenn diese Bibliothek intern mit Fehlern wie Unicode-Fehlern, E / A-Fehlern und SSL-Fehlern fehlschlägt, müssen Sie sie in einen bibliotheksspezifischen Fehlertyp konvertieren. , Der Benutzer dieser Bibliothek sollte nur diesen Fehler behandeln. Bei Bedarf stellt Rust eine Fehlerkette bereit, die bis zu dem Ort zurückreicht, an dem der Fehler erstellt wurde, und auf den ursprünglichen Fehler verweist.

Sie können den Typ "Box " auch überall verwenden. Wenn Sie keinen eigenen benutzerdefinierten Fehlertyp erstellen möchten, konvertiert dieser Typ ihn in einen beliebigen Fehler.

Während Python implizit Fehler weitergibt, verbreitet Rust Fehler explizit. Dies bedeutet, dass Sie sofort sehen können, dass die Funktion einen Fehler zurückgibt, auch wenn die Funktion keine Fehlerkontrolle hat. Dies wird durch das "try!" - Makro erreicht. Hier ist ein Beispiel:

    use std::fs::File;
    
    fn read_file(path: &Path) -> Result<String, io::Error> {
        let mut f = try!(File::open(path));
        let mut rv = String::new();
        try!(f.read_to_string(&mut rv));
        Ok(rv)
    }

Sowohl "File :: open" als auch "read_to_string" können mit E / A-Fehlern fehlschlagen. Das Makro try! Kehrt sofort von der Funktion zurück, um den Fehler bei einem Fehler nach oben zu verbreiten und bei Erfolg zu entpacken. Wenn das Ergebnis einer Funktion zurückgegeben wird, muss es entweder in "Ok" eingeschlossen werden, um den Erfolg anzuzeigen, oder in "Err", um den Fehler anzuzeigen.

Das Makro try! Ruft das Merkmal From auf, wodurch der Fehler übersetzbar wird. Implementieren Sie beispielsweise die Konvertierung von "io :: Error" in "MyError", indem Sie den Rückgabewert von "io :: Error" in "MyError" ändern und das Merkmal "From" implementieren. Und diese Konvertierung wird dort automatisch aufgerufen.

Alternativ können Sie den Rückgabewert von "io :: Error" in "Box " ändern, um einen Fehler zurückzugeben. Dies ist jedoch kein Kompilierungsfehler, sondern nur ein Laufzeitfehler.

Sie können das Ergebnis auch "auspacken ()", wenn Sie die Ausführung unterbrechen möchten, ohne den Fehler zu kontrollieren. Auf diese Weise erhält das Programm den Wert, wenn es erfolgreich ist, und wenn das Ergebnis ein Fehler ist, bricht es ab.

Veränderlichkeit und Eigentum

Der Teil der Sprache, der sich zwischen Rust und Python völlig unterscheidet, ist das Konzept der Variabilität und des Eigentums. Python ist eine Sprache mit einer Garbage Collection. Infolgedessen passiert zur Laufzeit fast alles mit dem Objekt. Sie können solche Objekte frei übergeben und sie bewegen sich "normal". Sie können explizit einen Speicherverlust verursachen, aber viele Probleme werden zur Laufzeit automatisch behoben.

Rust hat keinen Garbage Collector, aber die Speicherverwaltung erfolgt weiterhin automatisch. Dies wird durch ein Konzept ermöglicht, das als Eigentumsverfolgung bekannt ist. Alles, was Sie erstellen, gehört einem anderen. Wenn Sie dies in Python vergleichen, stellen Sie sich vor, dass alle Python-Objekte dem Interpreter gehören. Rusts Besitz ist viel lokaler. Angenommen, Sie haben eine Liste von Objekten mit Funktionsaufrufen. In diesem Fall besitzt die Liste das Objekt und der Funktionsumfang die Liste.

Komplexere Besitzerszenarien können durch Anmerkungen zur Lebensdauer des Eigentums und Funktionssignaturen dargestellt werden. Zum Beispiel haben wir im obigen Implementierungsbeispiel "Hinzufügen" den Empfänger "Selbst" genannt, genau wie Python. Im Gegensatz zu Python wird der Wert jedoch in eine Funktion "verschoben", während in Python die Methode mit einer Variablenreferenz aufgerufen wird. Dies bedeutet, dass Sie mit Python Folgendes können:

    leaks = []
    
    class MyType(object):
        def __add__(self, other):
            leaks.append(self)
            return self
    
    a = MyType() + MyType()

Verlässt sich selbst in die globale Liste, wenn MyType-Instanzen zu anderen Objekten hinzugefügt werden. Wenn Sie den obigen Code ausführen, werden zwei Verweise auf die erste Instanz von "MyType" angezeigt. Einer zu "Lecks" und der andere zu "a". Mit Rust geht das nicht. Weil es nur einen Besitzer gibt. Wenn Sie versuchen, "self" zu "leaks" hinzuzufügen, "verschiebt" der Compiler den Wert dorthin und die Funktion kann ihn nicht zurückgeben, da er irgendwohin verschoben wurde. Um diesen Wert von der Funktion zurückzugeben, müssen Sie ihn zuerst zurück verschieben (z. B. aus der Liste entfernen).

Was ist, wenn Sie zwei Verweise auf ein Objekt benötigen? Sie können diesen Wert tatsächlich ausleihen. Die Anzahl der unveränderlichen Kredite ist unbegrenzt, sondern nur ein veränderlicher Kredit (und nur, wenn kein unveränderlicher Kredit vorhanden ist).

Funktionen, die mit invarianter Ausleihe arbeiten, sind mit "& self" gekennzeichnet, und Funktionen, die eine variable Ausleihe erfordern, sind mit "& mut self" gekennzeichnet. Referenzen können nur vom Eigentümer gemietet werden. Wenn Sie diesen Wert aus dieser Funktion entfernen möchten (z. B. von der Funktion zurückgeben möchten), können Sie keine ausstehenden Kredite vergeben, und Sie verleihen diesen Wert, nachdem Sie das Eigentum irgendwo verschoben haben. Das kann ich auch nicht.

Dies ist eine große Veränderung in der Art und Weise, wie Sie über Programme denken, aber Sie werden sich bald daran gewöhnen.

Laufzeit-Ausleihen und mehrere Eigentümer [^ 1]

[^ 1]: Der Originaltext lautet "Mutible Owners", aber ich denke, es ist ein Fehler in Multiple.

Bisher wurde fast die gesamte Eigentumsverfolgung zum Zeitpunkt der Kompilierung überprüft. Aber was ist, wenn Sie den Besitz beim Kompilieren nicht überprüfen können? Es gibt mehrere Optionen für die kostenlose Nutzung. Ein Beispiel ist die Verwendung von Mutex. Der Mutex garantiert, dass nur eine Person zur Laufzeit eine variable Ausleihe für das Objekt hat, aber der Mutex selbst besitzt das Objekt. Auf diese Weise schreibe ich Code, der auf dasselbe Objekt zugreift, aber zu einem bestimmten Zeitpunkt kann nur ein Thread auf dieses Objekt zugreifen.

Dies bedeutet auch, dass Sie nicht vergessen, Mutex zu verwenden, und Datenkonflikte verursachen. Das liegt daran, dass solcher Code nicht kompiliert wird.

Aber wenn Sie das in Python tun möchten, wie können Sie den Besitzer des Speichers finden? Legen Sie in diesem Fall ein Objekt im Referenzzähler-Wrapper fest und verleihen Sie diesen Wert zur Laufzeit an diese Seite. Es kommt Pythons Verhalten sehr nahe, nur weil es zyklisch sein kann. Python teilt den Zyklus mit seinem Garbage Collector auf, und Rust hat keinen.

Um dies besser zu veranschaulichen, schauen wir uns ein komplexes Python-Beispiel und das Rust-Äquivalent an.

    from threading import Lock, Thread
    
    def fib(num):
        if num < 2:
            return 1
        return fib(num - 2) + fib(num - 1)
    
    def thread_prog(mutex, results, i):
        rv = fib(i)
        with mutex:
            results[i] = rv
    
    def main():
        mutex = Lock()
        results = {}
    
        threads = []
        for i in xrange(35):
            thread = Thread(target=thread_prog, args=(mutex, results, i))
            threads.append(thread)
            thread.start()
    
        for thread in threads:
            thread.join()
    
        for i, rv in sorted(results.items()):
            print "fib({}) = {}".format(i, rv)

Was wir hier tun, ist, 35 Threads zu erzeugen und einige schreckliche Berechnungen durchzuführen, um die Fibonacci-Zahl zu erhöhen. Verbinden Sie dann die Threads, um die sortierten Ergebnisse anzuzeigen. Eine Sache, die Sie hier bald bemerken werden, ist, dass es keine wesentliche Beziehung zwischen dem Mutex (Sperre) und dem resultierenden Array gibt.

Als nächstes ist ein Beispiel für Rust.

    use std::sync::{Arc, Mutex};
    use std::collections::BTreeMap;
    use std::thread;
    
    fn fib(num: u64) -> u64 {
        if num < 2 { 1 } else { fib(num - 2) + fib(num - 1) }
    }
    
    fn main() {
        let locked_results = Arc::new(Mutex::new(BTreeMap::new()));
        let threads : Vec<_> = (0..35).map(|i| {
            let locked_results = locked_results.clone();
            thread::spawn(move || {
                let rv = fib(i);
                locked_results.lock().unwrap().insert(i, rv);
            })
        }).collect();
        for thread in threads { thread.join().unwrap(); }
        for (i, rv) in locked_results.lock().unwrap().iter() {
            println!("fib({}) = {}", i, rv);
        }
    }

Der große Unterschied zum Python-Code besteht darin, dass anstelle einer Hash-Tabelle eine B-Tree-Map verwendet wird und diese Map dem Arc'ed-Mutex hinzugefügt wird. Was ist das? Zunächst verwende ich den B-Baum, weil er automatisch sortiert wird, was ich hier brauchte. Fügen Sie es dann zu Mutex hinzu, damit Sie die Karte zur Laufzeit sperren können. Die Beziehung wurde hier hergestellt. Fügen Sie diesen Mutex schließlich zu Arc hinzu. Die Bogenreferenz zählt, was sie einschließt. In diesem Fall Mutex. Dies bedeutet, dass wir garantieren, dass Mutex erst entfernt werden kann, nachdem der letzte Thread ausgeführt wurde. Es ist ein kluger Mechanismus.

Nun wollen wir sehen, wie dieser Code funktioniert. Zählen Sie wie Python bis zu 35 [^ 2] und führen Sie lokale Funktionen für jede Zahl aus. Im Gegensatz zu Python können Sie hier Verschlüsse verwenden. Erstellen Sie dann eine Kopie von Arc in Ihrem lokalen Thread. Dies bedeutet, dass jede Methode ihren eigenen Bogen einzeln sieht (intern erhöht dies automatisch die Referenzanzahl und verringert sie, wenn der Thread stirbt). Dann erzeugen Sie diesen Thread mit einer lokalen Funktion. Diese "Bewegung" weist Sie an, den Verschluss innerhalb des Fadens zu bewegen. Führen Sie dann die Fibonacci-Funktion in jedem Thread aus. Packen Sie den Bogen aus und fügen Sie ihn hinzu, wenn Sie den Bogen sperren, der das Ergebnis zurückgibt. Ignorieren Sie dieses Auspacken für einen Moment. Es verwirrt einfach das explizite Ergebnis. Der Punkt ist jedoch, dass Sie die resultierende Karte nur erhalten können, wenn Sie Mutex entsperren. Sie können nie vergessen, es versehentlich zu sperren!

[^ 2]: Der Originaltext sagt "wir zählen bis 20 wie in Python", aber ich denke, es ist wahrscheinlich ein Tippfehler von 35.

Sammeln Sie dann alle Threads in einem eindimensionalen Array (Vektor). Schließen Sie abschließend alle Threads iterativ an und zeigen Sie das Ergebnis an.

Hier sind zwei Dinge zu beachten. Es gibt nur sehr wenige sichtbare Schimmelpilze. Natürlich verarbeiten Arc-Typen und Fibonacci-Funktionen vorzeichenlose 64-Bit-Integer-Typen, aber es gibt keine anderen expliziten Typen. Ich habe auch die B-Tree-Map anstelle des Hash-Objekts verwendet, da Rust einen solchen Typ bereitstellt.

Das Iterieren funktioniert genau wie Python. Der einzige Unterschied besteht darin, dass Rust in diesem Beispiel einen Mutex erhalten muss. Der Grund ist, dass der Compiler nicht weiß, dass Sie den fertigen Thread oder seinen Mutex nicht benötigen. Es gibt jedoch APIs, für die dieser Mutex nicht erforderlich ist, und diese APIs sind in Rust 1.0 noch nicht stabil.

In Bezug auf die Leistung wird genau das passieren, was Sie erwartet haben. (In diesem Beispiel wurde absichtlich schrecklicher Code geschrieben, um das Verhalten des Threads zu veranschaulichen.)

Unicode

Das Thema Unicode ist mein Favorit :) Rust und Python sind ganz unterschiedlich. Python (sowohl 2 als auch 3) ist dem Unicode-Modell sehr ähnlich, das Unicode-Daten einem Array von Zeichen zuordnet. Rust hingegen ist eine Unicode-Zeichenfolge, die immer als UTF-8 gespeichert wird. Ich habe zuvor erklärt, warum dies eine viel bessere Lösung ist als das, was Python und C # versuchen (UCS vs UTF-8 als interne String-Codierung. Siehe 2014/1/9 / ucs-vs-utf8 /)). Eine sehr interessante Sache bei Rust ist der Umgang mit der hässlichen Realität der Welt rund um das Codieren.

Zunächst ist sich Rust völlig bewusst, dass die Betriebssystem-APIs (sowohl Windows Unicode als auch Linux Non-Unicode) ziemlich schrecklich sind. Im Gegensatz zu Python zwingen wir Unicode nicht in diese Bereiche. Stattdessen haben sie verschiedene Zeichenfolgentypen, die (vernünftigerweise) zu geringen Kosten ineinander konvertiert werden können. Dies funktioniert in der Praxis gut und macht die String-Manipulation sehr schnell.

Das Akzeptieren von UTF-8 für den größten Teil des Programms macht das Codieren / Decodieren überflüssig. Sie müssen nur kostengünstige Validierungsprüfungen durchführen, und die Verarbeitung von UTF-8-Zeichenfolgen muss nicht in der Mitte codiert werden. Wenn Sie die Windows Unicode-API integrieren müssen, können Sie intern zu relativ geringen Kosten WTF-8-Codierung in UCS2 wie UTF-16 konvertieren. -8 /) wird verwendet.

Sie können überall zwischen Unicode und Bytes konvertieren und Bytes nach Bedarf verarbeiten. Sie können dann später Validierungsschritte ausführen, um sicherzustellen, dass alles wie beabsichtigt ist. Auf diese Weise können Sie ein Protokoll schreiben, das sowohl sehr schnell als auch sehr praktisch ist. Vergleichen Sie dies damit, dass Sie in Python ständig codieren und decodieren müssen, um die Zeichenfolgenindizierung von O (1) zu unterstützen.

Neben dem wirklich guten Speichermodell von Unicode gibt es auch viele APIs für die Arbeit mit Unicode. Es ist entweder Teil der Sprache oder im Great crates.io-Index (https://crates.io/search?q=unicode). Dies umfasst das Falten von Groß- und Kleinschreibung, die Kategorisierung, kanonische Unicode-Ausdrücke, die Unicode-Normalisierung, bekannte URI / IRI / URL-APIs, die Aufteilung und die einfache Namenszuordnung.

Was sind die Nachteile? Sie können "ö" nicht rückgängig machen, wie Sie es mit einer Zeichenfolge wie "föo" [1] beabsichtigt haben. Aber auf jeden Fall ist es keine gute Idee.

Als Beispiel für die Interaktion mit dem Verhalten des Betriebssystems stellen wir eine Anwendung vor, die eine Datei im aktuellen Verzeichnis öffnet und deren Inhalt und Dateinamen anzeigt.

    use std::env;
    use std::fs;
    
    fn example() -> Result<(), Box<Error>> {
        let here = try!(env::current_dir());
        println!("Contents in: {}", here.display());
        for entry in try!(fs::read_dir(&here)) {
            let path = try!(entry).path();
            let md = try!(fs::metadata(&path));
            println!("  {} ({} bytes)", path.display(), md.len());
        }
        Ok(())
    }
    
    fn main() {
        example().unwrap();
    }

Alle E / A-Operationen verwenden das zuvor eingeführte "Path" -Objekt. Der interne Pfad des Betriebssystems wird ordnungsgemäß gekapselt. Es können Bytes oder Unicode sein oder etwas anderes, das das Betriebssystem verwendet. Es wird jedoch ordnungsgemäß formatiert, indem ".display ()" aufgerufen wird. Diese Methode gibt ein Objekt in der Zeichenfolge zurück, das sich selbst formatieren kann. Das ist nützlich. Der Grund dafür ist, dass Sie niemals versehentlich eine fehlerhafte Zeichenfolge verlieren, wie dies beispielsweise in Python 3 der Fall wäre. Eine saubere Trennung der Interessen.

Distributionen und Bibliotheken

Rust hat eine "Fracht", die virtualenv + pip + setuptools kombiniert. Nun, standardmäßig funktioniert es nur mit einer Version von Rust, es ist also nicht genau virtuell, aber ansonsten funktioniert es wie erwartet. Der Vorteil von Python besteht darin, dass Sie Abhängigkeiten für verschiedene Versionen der Bibliothek im Git-Repository oder im crates.io-Index erstellen können. Wenn Sie Rost von der Website heruntergeladen haben, wird der Befehl "Fracht" mitgeliefert, und alles sollte wie erwartet funktionieren.

Wird Rust Python ersetzen?

Ich glaube nicht, dass es eine direkte Beziehung zwischen Python und Rust gibt. Zum Beispiel war Python auf dem Gebiet der Informatik erfolgreich, und ich glaube nicht, dass Rust in naher Zukunft auf diesem Gebiet arbeiten wird, nur weil es so viel Arbeit erfordert. Ebenso macht es keinen Sinn, ein Shell-Skript in Python zu schreiben, während es in Rust geschrieben wird. Trotzdem denke ich, dass es anfängt, Rust in Bereichen zu betrachten, in denen es früher Python war, genauso wie viele Python-Programmierer angefangen haben, Go zu verwenden.

Rust ist eine sehr mächtige Sprache, hat ein starkes Fundament und arbeitet unter einer freien Lizenz mit einer freundlichen Gemeinschaft und einer demokratischen Haltung gegenüber der Sprachentwicklung.

Die Laufzeitunterstützung von Rust ist so gering, dass die Verwendung von Python über ctypes und CFFI sehr einfach ist. Ich könnte mir die Zukunft sehr klar vorstellen. Es wird ein Python-Paket erstellt, das eine Verteilung der in Rust geschriebenen Binärmodule enthält, die das Rust-Modul ohne den Aufwand des Entwicklers von Python aus aufrufen.

© Copyright 2015 by Armin Ronacher. Content licensed under the Creative Commons attribution-noncommercial-sharealike License.

Recommended Posts

[Übersetzung] Erste Schritte mit Rust für Python-Programmierer
Erste Schritte mit Python für PHPer-Klassen
Erste Schritte mit Python für PHPer-Funktionen
1.1 Erste Schritte mit Python
Erste Schritte mit Python
Erste Schritte mit Python für PHPer-Super Basics
Erste Schritte mit Python
Einstellungen für den Einstieg in MongoDB mit Python
Einführung in Python-Funktionen
Erste Schritte mit Python Django (1)
Erste Schritte mit Python Django (4)
Erste Schritte mit Python Django (3)
Einführung in Python Django (6)
Erste Schritte mit Python Django (5)
Erste Schritte mit Google App Engine für Python und PHP
Erste Schritte mit Python Responder v2
Erste Schritte mit Python-Webanwendungen
Erste Schritte mit Julia für Pythonista
Erste Schritte mit Python Grundlagen von Python
Erste Schritte mit genetischen Python-Algorithmen
Erste Schritte mit Python 3.8 unter Windows
Erste Schritte mit Python3 # 1 Grundkenntnisse erlernen
Verwenden Sie DeepL mit Python (für die Artikelübersetzung)
Erste Schritte mit Python Web Scraping Practice
Erste Schritte mit Python Web Scraping Practice
Erste Schritte mit Dynamo von Python Boto
Erste Schritte mit Lisp für Pythonista: Ergänzung
Erste Schritte mit Python mit 100 Klopfen bei der Sprachverarbeitung
Django 1.11 wurde mit Python3.6 gestartet
Erste Schritte mit der japanischen Übersetzung des Keras Sequential-Modells
Erste Schritte mit apache2
Erste Schritte mit Django 1
Einführung in die Optimierung
Erste Schritte mit AWS IoT in Python
Erste Schritte mit Numpy
Erste Schritte mit Spark
Erste Schritte mit Pydantic
Erste Schritte mit Jython
Materialien zum Lesen, wenn Sie mit Python beginnen
Erste Schritte mit Django 2
Erste Schritte mit Python3 # 2 Erfahren Sie mehr über Typen und Variablen
Übersetzen Erste Schritte mit TensorFlow
Einführung in Tkinter 2: Button
Erste Schritte mit Go Assembly
Python wurde von C-Programmierern gestartet
Beginnen Sie mit Python! ~ ② Grammatik ~
Erste Schritte mit Django mit PyCharm
Erste Schritte mit Python3 # 3 Versuchen Sie erweiterte Berechnungen mit der import-Anweisung
Erste Schritte mit Mathematik Beginnen mit Python Programming Challenge Persönliche Notizen-Problem 1-1
Prolog-Objektorientierung für Python-Programmierer
Beginnen Sie mit Python! ~ ① Umweltbau ~
Link, um mit Python zu beginnen
Einführung in Git (1) History-Speicher
Erste Schritte mit Sphinx. Generieren Sie Docstring mit Sphinx
Erste Schritte mit Sparse Matrix mit scipy.sparse
Erste Schritte mit Python