Von C zu Rust wechseln?



  • Rustiger schrieb:

    Ownership und Borrowing nachzubauen bringt nichts, es muss Teil des Sprachkerns sein. Beim Nachbauen hängt es wieder beim Entwickler, ob er dieses Feature nutzt oder nicht und das will man nicht.

    Schwachsinn. Ownership muss nicht im Sprachkern sein. Auch bei Gc-Sprachen versuche ich nach Moeglichkeit immer meine Programmstruktur auf je einen einzigen Besitzer zuzuschneiden, weil es die Anzahl moeglicher unberechtigter Zugriffe und damit Fehlerquellen minimiert.

    Rust und C++ haben das gleiche ownership-modell. Nach Moeglichkeit legt man das Objekt direkt an. Braucht man Zugriff, aber keine Kopie, verwendet man eine Referenz.
    Muss man etwas auf dem Heap reservieren, verwendet man fuer dynamische Arrays einen Container (vector oder Vec) und fuer einfache Objekte unique_ptr oder Box.
    Hat man mehrere moegliche Besitzer, nimmt man shared_ptr oder Rc.
    Erst zur Implementierung von diesen grundlegenden Strukturen oder fuer die Verwendung von C-Libraries greift man zu rohen pointern.

    Borrowing schuetzt lediglich vor Fluechtigkeitsfehlern. Normalerweise schaut man in die Referenz und sieht da, dass die pointer von vector beim push_back nicht erhalten bleiben und laesst es dann bleiben.



  • Rustiger schrieb:

    Ich bin nur Anwender und glaube da den Profis von Mozilla einfach mal.

    Warum glaubst du den Profis von Google nicht und verwendest Go?
    Dieses Borrow System funktioniert nur für triviale Fälle, die eh keinen interessieren.



  • Marthog schrieb:

    Borrowing schuetzt lediglich vor Fluechtigkeitsfehlern.

    Ist nicht der Sinn einer (Hoch-)Sprache, vor Flüchtigkeitsfehlern zu schützen?



  • ich warte lieber. Vielleicht wird eine Version von C++2x das haben, was Rust heute hat.



  • TyRoXx schrieb:

    Oh man, du hast ja mal gar keine Ahnung, worum es geht.

    Was willst du denn?



  • großbuchstaben schrieb:

    krümelkacker schrieb:

    großbuchstaben schrieb:

    welche Nische ist denn offen, die eine weitere Sprache wie Rust besetzen kann?

    [...] Rust will ein besserer C++ Ersatz sein, der es wie C++ auf "zero cost abstraction" aber zusätzlich auch auf Speichersicherheit und Threadsicherheit abgesehen hat.

    und wo ist da die offene Nische?

    Meinst du, daß sichere Software mit herkömmlichen Sprachen nicht möglich ist?

    Nein. Natürlich ist es theoretisch möglich ein Programm in einer anderen Sprache zu schreiben, was keinen Mist baut, auch in C++. Und das klappt sogar meistens in der Praxis. So verdiene ich mir immerhin meine Brötchen seit den letzten 8 Jahren. Ich denke aber, Du verwendest den Sicherheitsbegriff etwas anders als ich. Deswegen habe ich den hier jetzt vermieden.

    großbuchstaben schrieb:

    In C++ ist das nicht zuletzt eine Frage des Programmierstils bzw der Fähigkeiten des Entwicklers.

    Ja, das stimmt. C++ bietet 'ne Werkzeugkiste an, die, wenn man sie effektiv einzusetzen weiß, einem dabei hilft, keinen groben Mist zu bauen … meistens. RAII und Templates sind da in der Hinsicht ganz oben dabei unter den wichtigen C++ Patterns/Features, IMHO.

    Bzgl Nische: Rust ist für die Leute, die in erster Linie vom Compiler garantiert haben wollen, dass sie keine Datenrennen und keine „Speicherfehler“ (im Sinne der Speichersicherheit) produziert haben, ohne dabei auf die Performancevorteile zu verzichten, die sie von C++ gewohnt sind und ohne dabei die C-Altlast mitzuschleppen. (Und natürlich gibt es noch diverse andere Unterschiede, die auch nicht alle für Rust sprechen.)

    Cybertec schrieb:

    TyRoXx schrieb:

    Oh man, du hast ja mal gar keine Ahnung, worum es geht.

    Was willst du denn?

    Ich weiß auch nicht so ganz, was Du mit Deinem Kommentar gemeint hast. Das klang irgendwie so, als würdest Du glauben, Rust erkaufte sich ein gewisses Maß an Sicherheit durch einen Overhead zur Laufzeit. Das trifft jedenfalls nur auf das Bounds-Checking zu — nicht auf das, wobei einem der Borrow Checker zur Übersetzungszeit hilft. Rust lässt dich aber, wenn Du nach entsprechenden Profiling rausgefunden hast, dass irgendwo das Bounds-Checking doch was an der Laufzeit ausmacht (eher unwahrscheinlich), dann kannst du das da selektiv per unsafe auch quasi abschalten. Es vergrößert zwar die „Angriffsfläche“, aber dann gibst Du Dir halt Mühe, da keinen Bug einzubauen. Ggf kann man es in so einem Fall auch ohne unsafe lösen. Das Iterieren über ein Array per Iterator (statt Array Indexing je Element) ist sehr flott.



  • Ich lese hier schon eine Weile mit, aber so langsam platzt mir die Hutschnur. Die Rust-Leute klagen, wenn man etwas über Rust sagt, ohne Ahnung zu haben (verständlich), aber tun dann dasselbe bei C++?

    Rustiger schrieb:

    #include <iostream>
    #include <vector>
    #include <string>
    
    using namespace std;
    
    int main() {
        vector<string> v;
        v.push_back("Hello");
        string& x = v[0];
        v.push_back("world");
        cout << x;
    }
    

    Ersetzte vector durch deque und es kompiliert und läuft fehlerfrei. Und dass dieses Beispiel absolut willkürlich ist (man benutzt Referenzen als Variablen so gut wie nie) lasse ich mal aussen vor.

    Als Unwissender: Kann mir jemand erklären, wie ich in Rust println für meine eigenen Structs überladen kann?



  • Du meinst sowas hier? Ich habe das mal aus der Rust-Doku kopiert.

    use std::fmt;
    use std::f64;
    use std::num::Float;
    
    #[derive(Debug)]
    struct Vector2D {
        x: int,
        y: int,
    }
    
    impl fmt::Display for Vector2D {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            // The `f` value implements the `Writer` trait, which is what the
            // write! macro is expecting. Note that this formatting ignores the
            // various flags provided to format strings.
            write!(f, "({}, {})", self.x, self.y)
        }
    }
    
    // Different traits allow different forms of output of a type. The meaning
    // of this format is to print the magnitude of a vector.
    impl fmt::Binary for Vector2D {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            let magnitude = (self.x * self.x + self.y * self.y) as f64;
            let magnitude = magnitude.sqrt();
    
            // Respect the formatting flags by using the helper method
            // `pad_integral` on the Formatter object. See the method documentation
            // for details, and the function `pad` can be used to pad strings.
            let decimals = f.precision().unwrap_or(3);
            let string = f64::to_str_exact(magnitude, decimals);
            f.pad_integral(true, "", string.as_slice())
        }
    }
    
    fn main() {
        let myvector = Vector2D { x: 3, y: 4 };
    
        println!("{}", myvector);       // => "(3, 4)"
        println!("{:?}", myvector);     // => "Vector2D {x: 3i, y:4i}"
        println!("{:10.3b}", myvector); // => "     5.000"
    }
    


  • asfdlol schrieb:

    Die Rust-Leute klagen, wenn man etwas über Rust sagt, ohne Ahnung zu haben (verständlich), aber tun dann dasselbe bei C++? [...] Ersetzte vector durch deque und es kompiliert und läuft fehlerfrei. Und dass dieses Beispiel absolut willkürlich ist (man benutzt Referenzen als Variablen so gut wie nie) lasse ich mal aussen vor.

    Ich verstehe nicht, was du meinst.
    Referenzen benutzt "man" ständig.
    Ein Beispiel, warum das gefährlich ist:

    void find_similar_names(std::string const &name, std::vector<std::string> &results);
    

    Ist das hier sicher oder nicht? An der Signatur der Funktion kann ich das jedenfalls nicht erkennen. Und falls es sicher ist, wird möglicherweise innen drin eine unnötige Kopie gemacht.

    std::vector<std::string> names{"a"};
    find_similar_names(names.front(), names);
    

    In Rust würde etwas Äquivalentes nur kompilieren, wenn es sicher ist. Ohne Mehraufwand oder Laufzeitkosten kann leicht zu übersehendes undefiniertes Verhalten ausgeschlossen werden.

    fn find_similar_names(name: &str, results: &mut Vec<String>);
    

    asfdlol schrieb:

    Als Unwissender: Kann mir jemand erklären, wie ich in Rust println für meine eigenen Structs überladen kann?

    Das entsprechende Trait heißt inzwischen std::fmt::Display.



  • TyRoXx schrieb:

    asfdlol schrieb:

    Die Rust-Leute klagen, wenn man etwas über Rust sagt, ohne Ahnung zu haben (verständlich), aber tun dann dasselbe bei C++? [...] Ersetzte vector durch deque und es kompiliert und läuft fehlerfrei. Und dass dieses Beispiel absolut willkürlich ist (man benutzt Referenzen als Variablen so gut wie nie) lasse ich mal aussen vor.

    Ich verstehe nicht, was du meinst.
    Referenzen benutzt "man" ständig.
    Ein Beispiel, warum das gefährlich ist:

    void find_similar_names(std::string const &name, std::vector<std::string> &results);
    

    Ist das hier sicher oder nicht? An der Signatur der Funktion kann ich das jedenfalls nicht erkennen. Und falls es sicher ist, wird möglicherweise innen drin eine unnötige Kopie gemacht.

    Doch, das kann man sehr gut sehen, nämlich daran, dass die Referenz auf den Vektor nicht konstant ist. Das weglassen von const soll immer implizieren, dass du das Objekt verändern möchtest und in dem Moment weißt du, dass das gefährlich ist, Elemente des Vektors ebenfalls als Referenz zu übergeben.

    Edit: Falls du meinen solltest, dass ähnliche Namen in dem Array nicht gesucht werden sollen, sondern die Funktion die sich irgendwie aus den Fingern saugt, dann ist dieser Code noch konstruierter, denn niemand würde ein Result-Vektor erstellen, nur um dort den Namen drin zu speichern (warum auch immer man das tun sollte?), damit er am Ende sowieso mit Ergebnissen überschrieben werden soll.
    Und zu guter Letzt: In C++ benutzt man Rückgabewerte:

    vector<string> find_similar_names(const string&);
    

    Copy-Elision garantiert dir auch, dass da nichts kopiert wird. Alles supi.
    Programmierer, die in C++ solchen Code schreiben würden, würden auch in Rust totalen Mist zusammenschreiben. Bei denen scheitert's dann nämlich an den Grundlagen und da hilft dir keine Programmiersprache weiter.



  • Der Code sollte nicht besonders toll sein, sondern ist auf das Wesentliche reduziert. Wenn du willst, kannst du dir OutputIterator + back_inserter dazudenken.

    Jodocus schrieb:

    Doch, das kann man sehr gut sehen, nämlich daran, dass die Referenz auf den Vektor nicht konstant ist. Das weglassen von const soll immer implizieren, dass du das Objekt verändern möchtest und in dem Moment weißt du, dass das gefährlich ist, Elemente des Vektors ebenfalls als Referenz zu übergeben.

    Das ist doch reine Spekulation aufgrund von subjektiven Konventionen. Die Funktion könnte sehr wohl sicher implementiert sein. Vielleicht hat der Autor sich die Benutzung genau so gedacht, wie ich es geschrieben habe. Es gibt nur keine Möglichkeit das in C++ ohne einen Kommentar zu kommunizieren: Weder dem Compiler, noch dem Menschen. Beides geht in Rust in den meisten ähnlichen Fällen.



  • Jodocus schrieb:

    Doch, das kann man sehr gut sehen, nämlich daran, dass die Referenz auf den Vektor nicht konstant ist. Das weglassen von const soll immer implizieren, dass du das Objekt verändern möchtest und in dem Moment weißt du, dass das gefährlich ist, Elemente des Vektors ebenfalls als Referenz zu übergeben.

    Wie ist es mit folgendem Problem: Man hat eine GUI-Library, bei der jedes Objekt eine Liste mit Child-elemente sowie eine Event-listenern hat. Dann erstellt man einen Button, der beim Klicken ein Element loeschen soll (klassischer Close-Button von Fenstern). Der naive Ansatz ist, einen Eventlistener zu erstellen, der beim OnEvent den Loeschbefehl ausloest. Bei diesem Design kann es aber vorkommen, dass der EventListener seinen direkten Parent direkt oder indirekt loescht und diese aber noch in ihrer jeweiligen update-Methode stecken.
    In C++ ist das einfach hinschreibbar, denn beim Design der library denkt man, dass jedes Element einen owning-pointer auf die Childreferenzen hat und der Eventlistener einen non-owning auf das zu loeschende.
    In Rust protestiert der Compiler, sobald man das so aufgeschrieben hat und man muss sich schon einen anderen Designansatz ueberlegen.



  • @ TyRoXx:
    Ja, alles ziemlich an den Haaren herbeigezogen. Die Verwendung von back_inserter macht nur noch offensichtlicher, dass der Vektor verändert werden soll. Wie gesagt, keine Sau schreibt Code wie:

    vector<string> result; // hm, die API erwartet einen Result-vector, der dann befüllt wird
    result.push_back("Name"); // hm, pushen wir einfach mal aus Jucks den String da rein
    find_similar_names(result.back(), begin(result), end(result), back_inserter(result));
    

    Es gibt überhaupt keinen Grund, den String in den Vektor zu schreiben (außer man will einen flapsigen Versuch starten, ein Real-World-Problem in C++ zu demonstrieren).

    @ marthog: Irgendwie blöd, der Programmiersprache die Schuld am kaputten Klassendesign zu geben, oder?

    Sorry, ist mir alles zu blöd hier. Rust ist eine interessante Sprache, aber hier wird ja nur wieder mit religiösem Eifer versucht, Rust irgendwie zu rechtfertigen und als absoluten C++-Nachfolger zu propagieren, anstatt sich einfach mal mit den anderen Semantiken zu beschäftigen, ohne sofort wieder auf C++ rum zu bashen. Bin raus.



  • Jodocus schrieb:

    Es gibt überhaupt keinen Grund, den String in den Vektor zu schreiben (außer man will einen flapsigen Versuch starten, ein Real-World-Problem in C++ zu demonstrieren).

    Stimmt, ich hatte übersehen, dass es überhaupt keinen Grund gibt, Bugs bezüglich Speichersicherheit zu produzieren. Deswegen passt das ja auch nie.

    Es ist auch völlig unmöglich, dass der von mir gezeigte Code durch gut gemeinte, aber nicht zuende gedachte Refaktorierung entstanden ist.

    Jodocus schrieb:

    anstatt sich einfach mal mit den anderen Semantiken zu beschäftigen, ohne sofort wieder auf C++ rum zu bashen. Bin raus.

    Kann mir mal jemand eine Stelle in dem Thread zeigen, an der "auf C++ rumgebasht" wird?

    Was die "C++-Verteidiger" so von sich geben, liest sich ziemlich typisch. Da hätte man direkt aus einem C-vs-C++-Thread kopieren können. C++ durch Rust ersetzen, C durch C++, fertig ist der Real Programmer Talk. Alles, was neu ist, braucht kein Mensch. Das haben wir früher nicht gebraucht, das brauchen wir jetzt auch nicht. Aber da müsste man ja etwas Neues lernen. Wofür brauche ich dann noch meinen sechsten Sinn für undefiniertes Verhalten?

    Ich mache jedenfalls ständig in C++ Fehler, die mit Ownership zusammenhängen. Das wird auch nicht weniger mit der Zeit. Ich weiß nur inzwischen besser, wie man die Ursache findet. Am liebsten wäre es mir, wenn der Compiler mir dabei helfen würde. Schon sind wir bei Rust. Und ich bin nicht der einzige, der diesen Gedankengang nachvollziehen kann.



  • Man fängt halt an sich generell eine ablehnende Haltung gegenüber Rust anzueignen wenn man in jedem Thread komplett ot damit genervt wird.



  • TyRoXx schrieb:

    Ich mache jedenfalls ständig in C++ Fehler, die mit Ownership zusammenhängen.

    ich nicht. Wirklich nicht. Der letzte Absturz liegt bei mir mindestens 2 Monate zurück, und ich muß bei einem der zwei laufenden C++ Projekte ohne smart pointers und ohne C++11 auskommen. RAII heißt das Zauberwort.



  • großbuchstaben schrieb:

    TyRoXx schrieb:

    Ich mache jedenfalls ständig in C++ Fehler, die mit Ownership zusammenhängen.

    ich nicht. Wirklich nicht. Der letzte Absturz liegt bei mir mindestens 2 Monate zurück, und ich muß bei einem der zwei laufenden C++ Projekte ohne smart pointers und ohne C++11 auskommen. RAII heißt das Zauberwort.

    Dito. Und seit ich den Debug Modus der STL und häufige asserts benutze, habe ich eigentlich nur noch assertion failure. Sehr selten mal einen segmentation fault und dann ist der Fehler auch fast direkt ersichtlich.



  • Ich benutze sowieso shared_ptr wenn mehrere Objekte ein Objekt teilen und der Performance-Nachteil ist nichtmal im Ansatz messbar.



  • Ihr seid ja Helden. Ihr seid die ersten Menschen die keine Speicherfehler in große C++-Projekte einbauen. Ihr müsst Millionen verdienen.



  • In der Tat. Valgrind ist da übrigens sehr gut und das findet bei mir nie etwas.


Anmelden zum Antworten