Von C zu Rust wechseln?



  • 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.



  • @NewHope
    Du solltest nicht von dir auf andere schliessen.

    Das Problem dabei grössere C++ Projekte leakfrei zu bekommen ist nicht dass die Sache ansich so schwer wäre, sondern dass man mit so vielen Leuten zusammenarbeiten muss die es nie richtig gelernt haben, nicht richtig machen wollen oder nicht richtig machen können weil sie einfach zu doof dafür sind. (Interessehalber: welche dieser Eigenschaften ist es bei dir?)

    Im übrigen der selbe Grund warum grössere Projekte in anderen Sprachen auch nicht ordentlich hinhauen. Selbst in GC-Sprachen kann man schön Leaks bauen. Und Leaks sind ja auch nicht die einzige Fehlerkategorie unter der grössere Projekte öfter mal leiden.



  • Es tut mir Leid, aber wenn jemand von sich behauptet, hinsichtlich Speicherfehler, nie einen Fehler zu übersehen, dann glaube ich das halt nicht. Niemand entwickelt zu 100% sicher. Wenn dies tatsächlich mal jemanden gelingen sollte, dann sorgen andere Aspekte dafür, das Speicherfehler da rein kommen, von denen du ja einige genannt hast. Genau deswegen ist es doch ein genial, wenn eine Sprache diese Fehler zur Compilezeit mit Zero-Costs und ohne GC zu 100% eliminieren kann und genau das macht Rust.



  • NewHope schrieb:

    Es tut mir Leid, aber wenn jemand von sich behauptet, hinsichtlich Speicherfehler, nie einen Fehler zu übersehen, dann glaube ich das halt nicht.

    Behauptet auch keiner. Der erste Build crasht sehr häufig. Dann schmeiß ich den Debugger an, warte bis zur Debug Assertion, schau mir den Callstack und Locals an, facepalme und fixe ihn. Selten brauche ich aber auch länger.

    Niemand entwickelt zu 100% sicher. Wenn dies tatsächlich mal jemanden gelingen sollte, dann sorgen andere Aspekte dafür, das Speicherfehler da rein kommen, von denen du ja einige genannt hast.

    Das behauptet auch keiner. Aber ich persönlich kenne keine Releaseversion irgendeiner guten Bibliothek, die Speicherfehler hat, wenn ich sie richtig verwende. Keine einzige.

    Genau deswegen ist es doch ein genial, wenn eine Sprache diese Fehler zur Compilezeit mit Zero-Costs und ohne GC zu 100% eliminieren kann und genau das macht Rust.

    Ja, das spart einem die 10 Minuten Debugging.

    Rust ist aber trotzdem nicht per se sicher und C++ per se unsicher. Das ist einfach nur extremer Schwachsinn.



  • Man braucht sie nicht übersehen - wer den richtigen Programmierstil hat der baut per Design keine Speicherfehler. Möchte ich etwas exklusiv besitzen dann kopiere oder move ich es. Möchte ich es teilen dann verwende ich shared_ptr oder eine zentrale Verwaltungsinstanz mit garantierter Lebenszeit.



  • So ein Unsinn und wie entstehen die ganzen Speicherfehler dann? Sie entstehen weil man eben nicht immer auf alles achten kann.



  • NewHope schrieb:

    So ein Unsinn und wie entstehen die ganzen Speicherfehler dann? Sie entstehen weil man eben nicht immer auf alles achten kann.

    Was heißt hier ganzen Speicherfehler?
    Wie häufig sind denn deiner Meinung nach, Speicherfehler in fertigen C++ Projekten?



  • NewHope schrieb:

    So ein Unsinn und wie entstehen die ganzen Speicherfehler dann? Sie entstehen weil man eben nicht immer auf alles achten kann.

    Nein, weil die Leute keine Ahnung von (modernem) C++ zu haben. Man muss nicht auf alles achten sondern einfach nur grobe Fehler vermeiden.



  • hustbaer schrieb:

    Das Problem dabei grössere C++ Projekte leakfrei zu bekommen ist nicht dass die Sache ansich so schwer wäre, sondern dass man mit so vielen Leuten zusammenarbeiten muss die es nie richtig gelernt haben, nicht richtig machen wollen oder nicht richtig machen können weil sie einfach zu doof dafür sind.

    Genau, einerseits erlaubt C++ "falsches" Memory-Handling und die "doofen" Leute lernen nie, wie man es richtig macht, andererseits hat man in C++ Kommunikationsfehler bezüglich Ownership (Schnittstellen nicht ausreichend/korrekt dokumentiert). Mit unique_ptr/shared_ptr geht zwar vieles, aber selbst damit kann es zu Fehlern kommen.

    Beide Fehler passieren in Rust nicht.

    Bez. dem C++-Getrolle:

    Ethon schrieb:

    Man braucht sie nicht übersehen - wer den richtigen Programmierstil hat der baut per Design keine Speicherfehler.

    Ethon schrieb:

    weil die Leute keine Ahnung von (modernem) C++ zu haben. Man muss nicht auf alles achten sondern einfach nur grobe Fehler vermeiden.

    Klar kann man kleinere Ein-Mann-Projekte mit genügend Hingabe korrekt umsetzen. Es passieren halt auch wenig Kommunikationsfehler. Fakt ist, dass in C++ die Schnittstellen mit Sprachmitteln nicht hinreichend dokumentiert werden können. Eine zweite Persion hätte da aber grosse Schwierigkeiten durchzublicken.

    Nathan schrieb:

    Wie häufig sind denn deiner Meinung nach, Speicherfehler in fertigen C++ Projekten?

    Segfaults in clang und g++ sind während der Entwicklung nicht selten. Und die kriegt man nur durch mühsames Debuggen wieder raus.
    Beide Projekte müssten doch von Leuten programmiert sein, die nicht "doof" sind oder "keine Ahnung von C++ haben".

    Und ich möchte betonen, dass Speichersicherheit nicht nur mit Leaks/Double frees/Nullpointer-Dereferenzierungen/Dangling references/etc. zu tun hat, sondern auch mit Data-Races.



  • müssen schrieb:

    Nathan schrieb:

    Wie häufig sind denn deiner Meinung nach, Speicherfehler in fertigen C++ Projekten?

    Segfaults in clang und g++ sind während der Entwicklung nicht selten. Und die kriegt man nur durch mühsames Debuggen wieder raus.

    Richtig, während der Entwicklung. Nicht im fertigem Code.
    Oder meinste von Clang und GCC selber?

    Beide Projekte müssten doch von Leuten programmiert sein, die nicht "doof" sind oder "keine Ahnung von C++ haben".

    Nein. Die internal compiler errors sind idR nur bei sehr obskuren, neuen C++ Features. Die Implementierung ist dann natürlich nicht ausgereift, da passiert sowas.

    Und ja, in Rust ist das wohl schwerer bis nahezu unmöglich, so Fehler zu machen. Ist ja auch schön und gut. Aber stellt bitte nicht C++ so dar, dass dort zwangsläufig Fehler passieren müssen.

    Und ich möchte betonen, dass Speichersicherheit nicht nur mit Leaks/Double frees/Nullpointer-Dereferenzierungen/Dangling references/etc. zu tun hat, sondern auch mit Data-Races.

    Zu Data-Races hab ich kaum Erfahrung, kann da nichts zu sagen.

    Edit:

    müssen schrieb:

    hustbaer schrieb:

    Das Problem dabei grössere C++ Projekte leakfrei zu bekommen ist nicht dass die Sache ansich so schwer wäre, sondern dass man mit so vielen Leuten zusammenarbeiten muss die es nie richtig gelernt haben, nicht richtig machen wollen oder nicht richtig machen können weil sie einfach zu doof dafür sind.

    Genau, einerseits erlaubt C++ "falsches" Memory-Handling und die "doofen" Leute lernen nie, wie man es richtig macht.

    Rust erlaubt auch "falsches" Memory Handling in unsafe Blöcken. Und das Problem ist ja, dass es sich die "doofen" Leute zutrauen, mit rohen Pointern/in unsafe Blöcken zu arbeiten und das deshalb auch tun werden. "doofe" Programmierer werden immer Speicherfehler produzieren, egal welche Sprache.



  • Nein, der gcc ist nicht representativ für schönes C.
    Hier ein 50+ Zeilen if Statement:

    if (in != 0 && GET_CODE (in) == SUBREG
          && (subreg_lowpart_p (in) || strict_low)
    #ifdef CANNOT_CHANGE_MODE_CLASS
          && !CANNOT_CHANGE_MODE_CLASS (GET_MODE (SUBREG_REG (in)), inmode, rclass)
    #endif
          && contains_reg_of_mode[(int) rclass][(int) GET_MODE (SUBREG_REG (in))]
          && (CONSTANT_P (SUBREG_REG (in))
    	  || GET_CODE (SUBREG_REG (in)) == PLUS
    	  || strict_low
    	  || (((REG_P (SUBREG_REG (in))
    		&& REGNO (SUBREG_REG (in)) >= FIRST_PSEUDO_REGISTER)
    	       || MEM_P (SUBREG_REG (in)))
    	      && ((GET_MODE_PRECISION (inmode)
    		   > GET_MODE_PRECISION (GET_MODE (SUBREG_REG (in))))
    #ifdef LOAD_EXTEND_OP
    		  || (GET_MODE_SIZE (inmode) <= UNITS_PER_WORD
    		      && (GET_MODE_SIZE (GET_MODE (SUBREG_REG (in)))
    			  <= UNITS_PER_WORD)
    		      && (GET_MODE_PRECISION (inmode)
    			  > GET_MODE_PRECISION (GET_MODE (SUBREG_REG (in))))
    		      && INTEGRAL_MODE_P (GET_MODE (SUBREG_REG (in)))
    		      && LOAD_EXTEND_OP (GET_MODE (SUBREG_REG (in))) != UNKNOWN)
    #endif
    #ifdef WORD_REGISTER_OPERATIONS
    		  || ((GET_MODE_PRECISION (inmode)
    		       < GET_MODE_PRECISION (GET_MODE (SUBREG_REG (in))))
    		      && ((GET_MODE_SIZE (inmode) - 1) / UNITS_PER_WORD ==
    			  ((GET_MODE_SIZE (GET_MODE (SUBREG_REG (in))) - 1)
    			   / UNITS_PER_WORD)))
    #endif
    		  ))
    	  || (REG_P (SUBREG_REG (in))
    	      && REGNO (SUBREG_REG (in)) < FIRST_PSEUDO_REGISTER
    	      /* The case where out is nonzero
    		 is handled differently in the following statement.  */
    	      && (out == 0 || subreg_lowpart_p (in))
    	      && ((GET_MODE_SIZE (inmode) <= UNITS_PER_WORD
    		   && (GET_MODE_SIZE (GET_MODE (SUBREG_REG (in)))
    		       > UNITS_PER_WORD)
    		   && ((GET_MODE_SIZE (GET_MODE (SUBREG_REG (in)))
    			/ UNITS_PER_WORD)
    		       != (int) hard_regno_nregs[REGNO (SUBREG_REG (in))]
    						[GET_MODE (SUBREG_REG (in))]))
    		  || ! HARD_REGNO_MODE_OK (subreg_regno (in), inmode)))
    	  || (secondary_reload_class (1, rclass, inmode, in) != NO_REGS
    	      && (secondary_reload_class (1, rclass, GET_MODE (SUBREG_REG (in)),
    					  SUBREG_REG (in))
    		  == NO_REGS))
    #ifdef CANNOT_CHANGE_MODE_CLASS
    	  || (REG_P (SUBREG_REG (in))
    	      && REGNO (SUBREG_REG (in)) < FIRST_PSEUDO_REGISTER
    	      && REG_CANNOT_CHANGE_MODE_P
    	      (REGNO (SUBREG_REG (in)), GET_MODE (SUBREG_REG (in)), inmode))
    #endif
    	  ))
    

    https://github.com/gcc-mirror/gcc/blob/7057506456ba18f080679b2fe55ec56ee90fd81c/gcc/reload.c#L1056-L1110


Anmelden zum Antworten