IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Vous êtes nouveau sur Developpez.com ? Créez votre compte ou connectez-vous afin de pouvoir participer !

Vous devez avoir un compte Developpez.com et être connecté pour pouvoir participer aux discussions.

Vous n'avez pas encore de compte Developpez.com ? Créez-en un en quelques instants, c'est entièrement gratuit !

Si vous disposez déjà d'un compte et qu'il est bien activé, connectez-vous à l'aide du formulaire ci-dessous.

Identifiez-vous
Identifiant
Mot de passe
Mot de passe oublié ?
Créer un compte

L'inscription est gratuite et ne vous prendra que quelques instants !

Je m'inscris !

Mamadou Babaei : « C++ n'a pas besoin d'une nounou comme le vérificateur d'emprunts de Rust »
Faut-il vraiment changer de langage, ou simplement mieux maîtriser les outils existants ?

Le , par Stéphane le calme

58PARTAGES

23  0 
Alors que Rust s’impose comme le champion de la sécurité mémoire grâce à son système de vérification à la compilation, certains développeurs C++ refusent de se laisser reléguer au rang de programmeurs imprudents. Avec un ton provocateur, le développeur professionnel de jeu Mamadou Babaei défend la capacité du C++ à détecter et corriger les fuites mémoire sans avoir besoin d’un « gardien de mémoire » comme Rust. À travers une démonstration concrète avec _CrtDumpMemoryLeaks, il relance le débat : faut-il vraiment changer de langage, ou simplement mieux maîtriser les outils existants ?

Rust : un langage trop protecteur ?

Rust a été acclamé ces dernières années comme l’un des langages les plus sécurisés pour le développement système. Sa principale innovation ? Le borrow checker (littéralement vérificateur d'emprunts), ce mécanisme de vérification à la compilation qui empêche les erreurs de mémoire comme les accès concurrents non sécurisés ou les use-after-free.

Le vérificateur d'emprunts est une fonctionnalité essentielle du langage Rust et fait partie de ce qui rend Rust Rust. Il vous aide (ou vous oblige) à gérer la propriété. Comme le dit le chapitre 4 de « The Rust Programming Language », « La propriété est la caractéristique la plus unique de Rust, et elle permet à Rust de garantir la sécurité de la mémoire sans avoir besoin d'un collecteur mémoire ».

Le vérificateur d'emprunt est un composant du compilateur Rust dont le rôle est de vérifier à la compilation que l’usage de la mémoire est sûr, sans fuite ni corruption potentielle, sans exécution du programme. Il repose sur trois concepts clés :
  • Propriété (ownership) : chaque valeur a un propriétaire unique.
  • Emprunts (borrowing) : on peut prêter une valeur de manière mutable ou non mutable, mais pas les deux en même temps.
  • Durée de vie (lifetimes) : chaque référence a une durée de vie clairement définie et vérifiée.

Le borrow checker interdit à la compilation :
  • Les accès concurrents à une même valeur mutable (race conditions évitées).
  • Les références à des valeurs qui ont été libérées (dangling pointers).
  • La modification de données pendant qu'elles sont empruntées de façon immuable.
  • L’utilisation après déplacement (move), pour éviter la double libération.

Mais pour certains développeurs C++, ce système ressemble davantage à une « nounou » : une surprotection qui entrave la liberté de coder rapidement et efficacement. Selon eux, les développeurs expérimentés n’ont pas besoin d’un langage qui leur interdit de faire certaines choses – ils veulent juste des outils qui les aident à le faire mieux, sans être bridés.

Un faux procès fait au C++ ?

Depuis l’essor de Rust dans les milieux de la sécurité et du développement système, une idée reçue semble s’installer : le C++ serait intrinsèquement dangereux. Ses critiques mettent en avant la complexité de sa gestion manuelle de la mémoire, source supposée d’erreurs fréquentes et de vulnérabilités critiques.

Mais ce discours occulte une réalité plus nuancée : le C++ n’est pas dangereux par nature, il est exigeant. Comme le rappelle Babaei, les fuites mémoire et les erreurs d’allocation ne sont pas une fatalité si les développeurs exploitent les outils disponibles dans l’écosystème du langage.

Ainsi, plutôt que de dénigrer le C++ pour son absence de mécanismes de sécurité intégrés à la Rust, il conviendrait de promouvoir les bonnes pratiques de développement, les outils de débogage avancés et les bibliothèques modernes comme RAII, unique_ptr, shared_ptr, ou encore Valgrind.

Dans sa vidéo intitulée « Rust Devs Think We’re Hopeless; Let’s Prove Them Wrong (with C++ Memory Leaks)! », Mamadou Babaei défend avec humour et technicité la capacité des développeurs C++ à gérer efficacement la mémoire, sans recourir aux mécanismes de sécurité stricts de Rust. Il démontre comment détecter les fuites mémoire en C++ en utilisant l'outil _CrtDumpMemoryLeaks fourni par la bibliothèque d'exécution C (CRT) de Microsoft.

Et d'indiquer :

« Lorsque les développeurs Rust pensent à nous, les gens du C++, ils imaginent une lignée maudite - un traumatisme générationnel transmis de malloc à free. Pour eux, chaque ligne de C++ que nous écrivons est comme jouer à la roulette russe - sauf que les six chambres sont chargées de comportements non définis.

« Ils nous regardent comme si nous étions sans espoir. Comme si nous étions à deux doigts d'une thérapie. Mais vous savez quoi ? Nous n'avons pas besoin d'une nounou pour le compilateur. Pas de vérificateur d'emprunts. Pas de durée de vie. Pas de modèles de propriété. Pas de magie noire. Même Valgrind n'est pas nécessaire. Juste des pointeurs bruts, de la détermination brute, et un peu de santé mentale douteuse.

« Dans cette vidéo, je vais donc vous montrer comment traquer les fuites de mémoire comme si vous étiez né avec un pointeur dans une main et un débogueur dans l'autre ».


_CrtDumpMemoryLeaks : Un outil à (re)découvrir

La démonstration de Babaei repose sur un outil méconnu mais redoutablement efficace : la fonction _CrtDumpMemoryLeaks, intégrée à la CRT (C Run-Time Library) de Microsoft. Elle permet, dès la compilation en mode debug, de détecter automatiquement les blocs de mémoire non libérés avant la fin de l’exécution.

En couplant cet outil à des macros de suivi (_CRTDBG_MAP_ALLOC, new redéfini avec traçabilité), il devient possible de cartographier précisément l'origine d'une fuite mémoire. Cette démarche, bien que nécessitant une configuration spécifique, prouve que le C++ n’est pas dépourvu de garde-fous — il demande juste qu’on les active.

Démonstration : détection de fuites mémoire avec _CrtDumpMemoryLeaks

Babaei présente un exemple de programme C++ qui génère intentionnellement une fuite mémoire en allouant de la mémoire sans la libérer. Pour détecter cette fuite, il utilise les fonctions de débogage de la CRT.

Démonstration d'une fuite de mémoire en C++ simple et intentionnelle

Code C++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
/** 
* Une fuite de mémoire en C++ simple et intentionnelle 
*/ 
  
#include <atomic> 
#include <cstdint> 
#include <cstdlib> 
#include <ctime> 
#include <iostream> 
#include <string> 
#include <thread> 
  
static constexpr int32_t PASSWORD_LENGTH = 1024 * 1024; 
static constexpr std::chrono::milliseconds INTERVAL_MILLISECONDS{1}; 
  
std::string GeneratePassword(std::size_t length) { 
    const std::string charset{ 
        "0123456789" 
        "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 
        "abcdefghijklmnopqrstuvwxyz" 
    }; 
  
    const std::size_t maxIndex = charset.size(); 
    std::string result(length, '\0'); 
  
    for (std::size_t i = 0; i < length; ++i) { 
        result[i] = charset[rand() % maxIndex]; 
    } 
  
    return result; 
} 
  
int32_t main() { 
    std::srand(static_cast<uint32_t>(std::time(nullptr))); 
    std::atomic<bool> stop{false}; 
  
    std::thread inputThread([&stop]() { 
        std::cin.get(); 
        stop = true; 
        }); 
  
    while (!stop) { 
        std::string* password{new std::string(GeneratePassword(PASSWORD_LENGTH))}; 
  
        std::cout 
            << "Generated password:" 
            << "\n" 
            << *password 
            << "\n\n" 
            << "Press the 'Enter' key to interrupt!" 
            << std::endl; 
  
        std::this_thread::sleep_for(INTERVAL_MILLISECONDS); 
    } 
  
    inputThread.join(); 
    std::cout << "'Enter' key detected! Will stop!" << std::endl; 
  
    return 0; 
}

Une démonstration de _CrtDumpMemoryLeaks

Code C++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
/** 
* Une démonstration de _CrtDumpMemoryLeaks 
*/ 
#define MEMORY_LEAK_TRACKER 1 
  
#if MEMORY_LEAK_TRACKER 
#define _CRTDBG_MAP_ALLOC 
#include <crtdbg.h> 
  
#define DEBUG_NEW new (_NORMAL_BLOCK, __FILE__, __LINE__) 
#define new DEBUG_NEW 
#endif  /* MEMORY_LEAK_TRACKER */ 
  
#include <atomic> 
#include <cstdint> 
#include <cstdlib> 
#include <ctime> 
#include <iostream> 
#include <string> 
#include <thread> 
  
static constexpr int32_t PASSWORD_LENGTH = 64; 
static constexpr std::chrono::milliseconds INTERVAL_MILLISECONDS{1000}; 
  
std::string GeneratePassword(std::size_t length) { 
    const std::string charset{ 
        "0123456789" 
        "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 
        "abcdefghijklmnopqrstuvwxyz" 
    }; 
  
    const std::size_t maxIndex = charset.size(); 
    std::string result(length, '\0'); 
  
    for (std::size_t i = 0; i < length; ++i) { 
        result[i] = charset[rand() % maxIndex]; 
    } 
  
    return result; 
} 
  
int32_t main() { 
#if MEMORY_LEAK_TRACKER 
    int dbgFlags = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); 
    dbgFlags |= _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF; 
    _CrtSetDbgFlag(dbgFlags); 
  
    // Only track one allocation per run! 
    // e.g. allocation number that causes leaks: 
    //_CrtSetBreakAlloc(162); 
#endif  /* MEMORY_LEAK_TRACKER */ 
  
    std::srand(static_cast<uint32_t>(std::time(nullptr))); 
    std::atomic<bool> stop{false}; 
  
    std::thread inputThread([&stop]() { 
        std::cin.get(); 
        stop = true; 
        }); 
  
    while (!stop) { 
        std::string* password{new std::string(GeneratePassword(PASSWORD_LENGTH))}; 
  
        std::cout 
            << "Generated password:" 
            << "\n" 
            << *password 
            << "\n\n" 
            << "Press the 'Enter' key to interrupt!" 
            << std::endl; 
  
        std::this_thread::sleep_for(INTERVAL_MILLISECONDS); 
    } 
  
    inputThread.join(); 
    std::cout << "'Enter' key detected! Will stop!" << std::endl; 
  
#if MEMORY_LEAK_TRACKER 
    _CrtDumpMemoryLeaks(); 
#endif  /* MEMORY_LEAK_TRACKER */ 
  
    return 0; 
}

Explication

Code C++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
#define MEMORY_LEAK_TRACKER 1 
  
#if MEMORY_LEAK_TRACKER 
#define _CRTDBG_MAP_ALLOC 
#include <crtdbg.h> 
  
#define DEBUG_NEW new (_NORMAL_BLOCK, __FILE__, __LINE__) 
#define new DEBUG_NEW 
#endif  /* MEMORY_LEAK_TRACKER */
Imaginons que nous construisions des maisons en LEGO et que nous oublions parfois de nettoyer les briques après la construction. Ce code dit : « Hé, activons un traqueur de briques LEGO ! ».

Il ajoute une caméra magique (<crtdbg.h>) qui surveille l'endroit où nous laissons tomber les briques. Et lorsque nous utilisons de nouvelles briques pour construire quelque chose, il ajoute secrètement qui l'a construit et où dans le code. Ainsi, si nous oublions de nettoyer, nous saurons exactement qui a fait le désordre et .

Explication technique :
  • _CrtSetDbgFlag(...) : Définit les drapeaux d'exécution pour activer le suivi des fuites de mémoire et le signalement automatique des fuites à la sortie du programme.
  • DBG_ALLOC_MEM_DF : Indique au tas de débogage de garder une trace des allocations de mémoire.
  • DBG_LEAK_CHECK_DF : Appelle automatiquement _CrtDumpMemoryLeaks() à la sortie de main().
  • _CrtSetBreakAlloc(162) : Fixe un point d'arrêt à la 162e allocation (telle qu'elle est comptée en interne par le tas CRT). Lorsqu'il est atteint, Visual Studio interrompt l'exécution afin que vous puissiez inspecter l'allocation à l'origine du problème. Vous pouvez commenter cette ligne sauf si vous essayez d'isoler une fuite spécifique dans un gros programme.

tl;dr ; _CrtSetDbgFlag(...) permet le suivi du tas au moment de l'exécution et les rapports de fuite ; _CrtSetBreakAlloc(N) vous permet de casser sur un numéro d'allocation spécifique.

Code C++ : Sélectionner tout
1
2
3
4
5
6
#if MEMORY_LEAK_TRACKER 
    _CrtDumpMemoryLeaks(); 
#endif  /* MEMORY_LEAK_TRACKER */ 
  
    return 0; 
}

[QUOTE]À la toute fin, nous disons à la caméra : « Ok, montre-moi tout ce qu'on a oublié de nettoyer. »

Elle envoie un rapport disant : « Hé ! Vous avez laissé une brique à la ligne 42 ! Et une autre à la ligne 57 ! »

Explication technique :
[LIST][*]Déclenche manuellement un rapport de fuite juste avant l'arrêt du programme.[*]C'est optionnel si vous avez déjà activé _CRTDBG_LEAK_CHECK_DF - le CRT va automatiquement faire le dumping des fuites.[*]Mais l'appeler[/*]...
La fin de cet article est réservée aux abonnés. Soutenez le Club Developpez.com en prenant un abonnement pour que nous puissions continuer à vous proposer des publications.

Une erreur dans cette actualité ? Signalez-nous-la !

Avatar de smarties
Expert confirmé https://www.developpez.com
Le 12/05/2025 à 12:26
Je faisait un peu de C++ dans les années 2000 et maintenant je suis passé sur Rust.

Rust met beaucoup de chose en place pour facilité son adoption :
- une documentation centralisée
- un gestionnaire de dépendances
- le Rust book qui donne beaucoup d'éléments pour démarrer avec le langage dans de bonnes conditions
- des erreurs de compilations explicites
- cargo clippy

Mes derniers souvenirs avec C++ sont moins favorables :
- documentation éclatée
- quand on reprend un projet pour le compiler, j'ai toujours eu à chercher les dépendances qu'il me manquait car ce n'était pas noté sur le projet ou c'était incomplet

Vu que je ne touche plus au C++ depuis longtemps, je ne suis pas du tout informé des outils qui existent (à part des quelques témoignages/commentaires/news lus ici). Si on se base sur les CVE, il est possible que beaucoup de ces merveilleux outils n'ait pas été utilisé pour empêcher les fuites.
7  0 
Avatar de prisme60
Membre régulier https://www.developpez.com
Le 12/05/2025 à 14:54
Quand un code atteint des milliers de lignes de code, avec un historique de plus de 10 ans, avec le passage d'une dizaine de développeurs ayant chacun sa manière de coder, la dette technique est colossale.
  • En c++, rien ne t'oblige à utiliser les casts du C++, c'est tellement plus facile d'utiliser les casts du C.
  • On s'aperçoit aussi que les développeurs n'utilisent pas suffisamment les const/constexpr.
  • Que certaines personnes n'ont pas compris l'intérêt des std::shared_ptr/std::weak_ptr.
  • Certains écrivent encore du code avec les itérateurs begin() et end(), alors que les std::range pourraient être utilisés.
  • L'utilisation de nullptr / -1 pour indiquer que l'objet / variable primitive n'est pas valorisée, alors que le std::optional existe justement pour ces cas de figure.
  • Les namespaces permettent de bien cloisonnées les noms. Insuffisamment utilisés dans le code, on trouve parfois même des "using namespace std" partout dans le code, ce qui va à l'encontre de l'usage du namespace.
  • Personnellement, j'ai toujours eu du mal à bien utilisé le std::forward().

Le c++ est devenu une usine à gaz. Les modules ne mélangent très mal avec le code c++ existant. Les temps de compilations sont catastrophiques. La librairie std gère mal le mixe de chaine MBCS / Unicode. L'utilisation des streams C++ sont lourds. Le langage accuse l'héritage des années. La gestion de l'asynchrone avec les lambdas nécessite une réflexion complexes sans garde-fou.
Je déconseille le développement en C++ si il n'y a pas de bonne raison. Autant s'orienter sur du C#, du Go, du Rust, en fonction des besoins (IHM, Web, Embarqué) et de ou des plateformes visées (Certains langages ont été pensés portables, et permettent d'utiliser une librairie, avec une simple inclusion).

Pour en revenir au sujet de l'article, il est évident que si il suffisait de juste faire attention à la gestion de la mémoire pour qu'il n'y ai pas de bug, il n'y aurait aucune CVE sur les fuites mémoires, les use-after-free, les double-free. Rust avec son borrow checker fait des merveilles, et permet des optimisations qui seraient déclarées comme dangereuses sans ce filet de sécurité! Le meilleur exemple concerne les sous-chaine de caractères. En c++, on a le std::string_view, et en Rust on a les slice. Si on détruit la source de la std::string_view, on se retrouve avec un use-after-free. En revanche, Rust lui ne compile pas dans ce cas là.
7  0 
Avatar de koala01
Expert éminent sénior https://www.developpez.com
Le 12/05/2025 à 10:27
Salut,

Faut-il vraiment changer de langage, ou simplement mieux maîtriser les outils existants ?
Il faudrait surtout bien entraîner les développeurs débuttants
  • en leur faisant comprendre dés le début que ce qui peut échouer le fera obligatoirement
  • qu'on n'est pas dans un monde de bisounours et qu'ils doivent donc considérer toutes les ressources externes (introduction utilisateur, allocation de mémoire, accès réseaux ou fichiers) comme suspectes
  • en leur faisant comprendre qu'ils ne sont pas meilleurs que les utilisateurs lambda et qu'ils seront les premiers à faire des erreurs, mais que leurs erreurs peuvent mener à la catastrophe

Et, pour ce qui est spécifique au C++ en leur apprenant directement le langage de manière moderne, et non tel qu'on nous l'a appris à la fin du siècle dernier

Ce qui mènera forcément à la deuxième solution : une meilleure maîtrise des outils existants
Le borrow checker de Rust est-il un mal nécessaire ou une entrave à la créativité ?
Ce n'est sans doute pas une entrave à la créativité.

Par contre, c'est sûrement un mal rendu nécessaire par le manque de maîtrise des outils existants.

Cependant, ce n'est au final qu'un emplâtre sur une jambe de bois, car il ne résout pas le problème de fond : les gens manquent cruellement de l'entrainement nécessaire à l'utilisation correcte des outls existants.
Peut-on vraiment comparer deux langages à des stades aussi différents de maturité ?
Peut on seulement comparer deux langages de programmation quels qu'ils soient autrement que par leur philosophie ou par leur "performances brutes" (vitesse d'exécution, consommation de mémoire et consommation électrique)

Les outils de détection de fuites mémoire en C++ sont-ils suffisants pour garantir une gestion sûre de la mémoire dans des projets complexes ?
Très certainement.

Mieux encore, C++ dispose depuis près de quinze ans de nombreux outils capables de les éviter...

La complexité du système de propriété de Rust justifie-t-elle son adoption face à la flexibilité de C++ ?
Si cela peut rassurer les afficionados de Rust, pourquoi pas

Sans doute beaucoup moins pour ceux qui gardent en tête que tout se paye; ou que le fait de contourner un problème n'est pas forcément une solution à long terme
Rust est-il adapté aux débutants ou réservé aux ingénieurs systèmes expérimentés ?
C'est un très vaste débat, que l'on pourrait avoir à propos de nombreux langages de programmations sensés "simplifier le travail" du développeur.

Certains argueront que ces "facilités" permettent d'apprendre plus facilement le langage, qu'elles permettent d'obtenir plus facilement ou plus rapidement un résultat "correct et sécurisé".

D'autres prétendront qu'il ne sert à rien de faciliter le travail du dévoppeur si cela implique qu'il puisse ne pas maîtriser (voir qu'il puisse ne pas avoir conscience) des concepts de base du développement.

Les premiers répondront sans doute que les langages les plus simples sont particulièrement adpatés aux débuttants; les seconds répondront plutôt qu'ils ne devraient être utilisés que par des personnes chevronnées.

Encore une fois, il s'agit essentiellement d'une question de philosophie
7  1 
Avatar de Rep.Movs
Membre actif https://www.developpez.com
Le 12/05/2025 à 13:50
Je trouve que les réactions épidermiques sont dommageables.

Rust ou C++, ou Java, ou Python sont liés à un besoin (bien sûr, pour python c'est une plaisanterie). Chaque langage a ses spécificités et certains avantages.
Mais à chaque fois que j'ai programmé dans un langage ou un autre, j'ai pu profiter des atouts du langages - jusqu'à me trouver face à une limite de ce langage, que ce soit une limite du langage même ou une difficulté dans la maintenance.

Concernant les problèmes de mémoire, si je déteste en général faire du web, je trouve que cela amène une vraie question dans la conception et l'écriture (présente aussi sous Android): l'unité de travail.

Si on se recentre sur les unités de travail et la durée de vie de nos objets (au sens conceptuel, pas d'implémentation), en général on comprend mieux comment gérer la mémoire.

Et si on a l'avantage d'être dans un environnement qui force à cette prise en compte, voire qui carrément isole chaque unité de travail, on est dans un environnement plus sûr pour développer.

A ce sujet, on peut se rappeler du fork() qui était une implémentation du unit of work.

Dans plusieurs développements, j'ai préférer sacrifier un peu les perfs en me basant sur des exécutables "jetables" pour augmenter la résilience - et ça marche très bien, même avec des langages comme VB6
5  0 
Avatar de jo_link_noir
Membre expert https://www.developpez.com
Le 13/05/2025 à 18:54
Je ne comprends, le monsieur dit qu'on a pas besoin d'être tenu par la main comme en Rust parce que C++ possède des outils pour faire la même chose.

Des outils qui demandent à changer le code. Des outils spécifiques Windows¹ et qui ne fonctionnent qu'en debug (voir la doc). Des outils qui font des vérifications uniquement runtime et qui demande donc l'exécution de toutes les branches. Des outils qui ne fonctionnent probablement qu'avec l'allocateur par défaut².

Mais comme il y a des outils, on a pas besoin que cela soit intégré au langage. Parce qu'un compilateur, ce n'est pas un outil... Comment ça si ? Parce qu'une vérification faîte à la compilation, c'est pareil qu'un truc imparfait fait au runtime... Comment ça non ?

Finalement, quelle différence entre utiliser un outil externe et avoir un outil directement intégré par le compilateur voir mieux, dans le langage ? À part que le premier est juste ultra pénible à mettre en œuvre et avec plein de contrainte, je n'en vois pas.

¹ Sur Linux et MacOS, il y a -fsanitize=leak (activé également avec -fsanitize=address sur Linux). Le compilateur injecte de lui-même du code sans que le code ait besoin d'être modifié. Pour du code externe non compilé avec cette option, il y a toujours Valgrind, Heaptrack ou Dr Memory (aussi dispo sur Windows). Comme /fsanitize=address existe depuis peu sur cl, il y aura peut-être un leak dans un avenir proche.
² c'est lié au CRT, je ne suis pas certain que changer l'allocateur par mimalloc est compatible avec cette fonction.
4  0 
Avatar de fdecode
Membre habitué https://www.developpez.com
Le 14/05/2025 à 10:13
Citation Envoyé par Stéphane le calme Voir le message
Et si le problème était culturel, pas technique ?

Le débat entre Rust et C++ révèle autant une opposition technique qu’une différence d’approche philosophique. Rust adopte une logique « zéro confiance », où le compilateur est conçu pour interdire les erreurs dès la phase de développement. Le développeur est contraint par le système de type, le borrow checker et les règles strictes de propriété.

À l’inverse, C++ s’inscrit dans une tradition où le développeur est empowered : il est maître de la mémoire, du cycle de vie des objets, et libre d’enfreindre les règles – pour le meilleur ou pour le pire.
Il y a quelque chose de culturel, sans doute.
Ce qui me plait dans le langage Rust, ce n'est pas strictement la logique de sécurisation de la mémoire à la compilation, mais l'agencement global des fonctionnalités du langage (syntaxe, paradigmes, compilateur, centralisation des dépôts autour de crates.io, documentation...). Et ce qui m'a amené à abandonner le C++, ce n'est pas le manque de borrow checker, mais un agencement de défauts "mineurs" qui au final ont fini par particulièrement m'énerver. Je ne reviendrai pas en arrière.
Au delà de ça, je dirais qu'une maîtrise suffisante (pour l'exigence de programmation que je me fixe) du C++ nécessiterait d'être spécialiste, alors que j'atteins la maîtrise qui me convient avec Rust sans être spécialiste. Mon activité ne me porte pas à être spécialiste d'un langage (et je ne le souhaite pas), en revanche j'ai besoin de bien maîtriser certaines capacités (calcul intensif, parallélisation, concurrence, GUI, natif ou Wasm, ...). D'autres langages m'ont apporté le confort de programmation souhaité, mais pas avec une approche bas niveau.

Citation Envoyé par Stéphane le calme Voir le message
Cette liberté est précieuse pour les experts, mais redoutable pour les débutants.
Plus de liberté en C++, sans doute, mais cela se justifie-t-il par la finalité?
Le mode unsafe de Rust donne cette liberté, mais est véritablement réservé à des experts.
Et pour autant, je ne dirais pas non plus que Rust en mode safe soit un langage pour débutant.

Citation Envoyé par Metal3d Voir le message
Et je n'ai pas la garantie qu'il fait mieux que moi puisque c'est assez difficile de s'écarter de sa gestion.
On en vient au problème de finalité dont je parle. En réalité, si on prend en compte les optimisations du compilateur, notre liberté de programmation ne nous apporte pas forcément une amélioration notable par rapport à une programmation contrainte. On sait d'ailleurs que la sémantique de prêt de Rust permet au compilateur des optimisations comparables à celle du mot clef restrict du C (ou des équivalents spécifiques du C++). Le juge de paix est au final la performance de ce code optimisé en fin de production.
4  0 
Avatar de jo_link_noir
Membre expert https://www.developpez.com
Le 14/05/2025 à 18:12
Citation Envoyé par koala01 Voir le message
A quel point faudrait-il effectivement être un spécialiste du C++ "moderne" , pour autant que vous connaissiez les pointeurs intelligents la bibliothèque standard en général et les références et que vous les utilisiez ... intelligemment pour atteindre les exigeances que vous vous fixez ?
Le problème étant de considérer qu'il ne faut pas une certaine maîtrise pour utiliser intelligemment les pointeurs intelligents et les références. Les pointeurs intelligents, encore, ça passe. Il y aura des d'erreurs du compilateur principalement à cause de la sémantique de déplacement de unique_ptr. Et là, on peut déjà se retrouver avec des gens qui passent par shared_ptr parce qu'ils ne comprennent ce que cela implique.

Par contre, les références — et de manière plus générale les vues (std::string_view, std::span, std::mdspan, std::function_ref, std::views, etc) — ont quelque chose d'implicite, mais cruciale: une dépendance sur la lifetime. Le compilateur ne va pas du tout aider sur les problèmes de lifetime. Rien que séparer en 2 lignes un appel comme foo(expression_vraiment_longue) peut amener à une UB car là où en une seule expression il n'y avait pas de problème de lifetime¹, on se retrouve avec un objet détruit dès la fin de ligne qui déclare la variable. Sans parler des subtilités de construction du genre T(...) vs T{...}² qui font une extension de la durée de vie des temporaires pour le premier, mais pas pour le second... En l'absence de constructeur.

Donc non, ce n'est pas simple. Même en connaissance tous les mécanismes, l'erreur est facile. À contrario, le compilateur de rust va être particulièrement intransigeant sur ce genre d'erreur.

Citation Envoyé par koala01 Voir le message
A quel point devrez vous effectivement être un expert de Rust pour pouvoir utiliser son mode unsafe de manière "suffisamment sécuritaire" ?
Contrairement à C++ qui a "bien défini", "fonction de l'implémentation" (ou on sait pas ce que ça fait, mais le scope est limité), "comportement non défini", j'ai l'impression qu'en rust il n'y a que "bien défini" et UB. Je trouve aussi que la doc est trop légère sur cette partie et cela rend compliqué de savoir la sémantique exacte de ce que l'on fait quand on fait vraiment du bas niveau. Après, les besoins de unsafe sont assez restreint, pour beaucoup de fonction c'est un simple marqueur pour dire "attention, si les paramètres d'entrés ne sont pas bons, cela est à tes risques". Exemple: vec::get(i) -> Option<&T> vs unsafe vec::get_unchecked(i) -> &T (l'équivalent de vec[i] en C++).

¹ Et encore, même avec une seule expression on peut avoir des problème de lifetime for (auto part : str + "abc") -> oups, dangling reference avant C++23 (https://en.cppreference.com/w/cpp/la...ge_initializer)
² https://en.cppreference.com/w/cpp/la...initialization (voir l'exemple avec struct B)
4  0 
Avatar de Metal3d
Membre habitué https://www.developpez.com
Le 13/05/2025 à 22:11
Citation Envoyé par smarties Voir le message
Je faisait un peu de C++ dans les années 2000 et maintenant je suis passé sur Rust.

Rust met beaucoup de chose en place pour facilité son adoption :
- une documentation centralisée
- un gestionnaire de dépendances
- le Rust book qui donne beaucoup d'éléments pour démarrer avec le langage dans de bonnes conditions
- des erreurs de compilations explicites
- cargo clippy

Mes derniers souvenirs avec C++ sont moins favorables :
- documentation éclatée
- quand on reprend un projet pour le compiler, j'ai toujours eu à chercher les dépendances qu'il me manquait car ce n'était pas noté sur le projet ou c'était incomplet

Vu que je ne touche plus au C++ depuis longtemps, je ne suis pas du tout informé des outils qui existent (à part des quelques témoignages/commentaires/news lus ici). Si on se base sur les CVE, il est possible que beaucoup de ces merveilleux outils n'ait pas été utilisé pour empêcher les fuites.
C'est justement ce que dit l'article. Le souci est moins technique que culturel. On peut faire du code fiable dans n'importe quel langage du moment que l'on maîtrise le sujet. Rust tient un peu trop la main et me supprime le plaisir de gérer moi même certains aspects. Et je n'ai pas la garantie qu'il fait mieux que moi puisque c'est assez difficile de s'écarter de sa gestion.

Je n'aime pas le Rust...
3  0 
Avatar de fdecode
Membre habitué https://www.developpez.com
Le 15/05/2025 à 11:27
Citation Envoyé par koala01 Voir le message
A quel point faudrait-il effectivement être un spécialiste du C++ "moderne" , pour autant que vous connaissiez les pointeurs intelligents la bibliothèque standard en général et les références et que vous les utilisiez ... intelligemment pour atteindre les exigeances que vous vous fixez ?
Merci à jo_link_noir pour sa réponse, sur un sujet que je ne connais que superficiellement (J'ai essentiellement arrêté avec C++ en 2011, même si je garde un œil sur l'évolution du standard).
Mais au delà de la gestion de la mémoire, c'est un problème global, et je suis désolé de cette réponse un peu longue.
Mais cela touche à ce qui m’a fait adopter Rust durablement, après un parcours qui est passé par C/C++, Java, Scala… et qui s’est arrêté sur Rust, justement parce qu’il me permet d’aller loin sans être un expert.

  1. Une expertise encapsulée plutôt qu’exigée
    Rust n’est pas un langage simple, mais il rend explicite ce que C++ laisse implicite. Et pour moi, c’est la grande différence :
    • En Rust, les contraintes de propriété, d’emprunt, de thread safety, etc., sont vérifiées et documentées par le compilateur. On apprend en écrivant.
    • En C++, j’ai toujours eu le sentiment inverse : dès qu’on touche à la concurrence, à la généricité avancée ou à l’interop système, on entre dans une zone grise où il faut être un vrai spécialiste (voire lire les standards pour éviter des UB invisibles).

    Autrement dit : je suis allé plus loin en Rust en tant qu’autodidacte que je ne me le suis jamais permis en C++.

  2. Une expressivité moderne et maîtrisée
    Ce qui m’a surpris en Rust, c’est le niveau de structuration et de généricité qu’on peut atteindre sans tomber dans le piège du “template spaghetti” :
    • Traits + impl + conditions (where) donnent une généricité claire et contrôlée.
    • Les macros (déclaratives ou procédurales) sont puissantes, mais bien encadrées.
    • La syntaxe est homogène : pas de vieux artefacts syntaxiques à gérer pour cause de rétrocompatibilité.

  3. Multithread, concurrence, async : confiance après compilation
    Un des points qui m’a bluffé avec Rust, c’est ça : une fois que ça compile, ça marche.
    Bien sûr, les erreurs algorithmiques restent de ma responsabilité — le compilateur ne fait pas mon travail à ma place. Mais il m’évite toute une classe de bugs insidieux (data races, use-after-free, invalid memory access, etc.) qui me ralentissaient ou me forçaient à multiplier les précautions et les couches de complexité en C++. Et ça change tout en termes de confiance dans le code produit.
    • Je fais du multithread et de l’async sans crainte, et je n’ai pas l’impression de marcher sur des œufs.
    • Je n’ai pas besoin d’un sanitiseur externe ou d’une expertise en modèles mémoire pour écrire du code concurrent correct.
    • Je n’imagine pas m’aventurer dans l’équivalent C++ sans m’entourer d’outils de debug lourds ou d’une aide externe aguerrie aux arcanes du standard.

    Et surtout : le compilateur me forme, là où en C++ j’ai l’impression que le compilateur me laisse “libre”, parfois au point de me perdre dans les conséquences implicites.

  4. L’écosystème : cargo versus cmake/make/…
    Un point souvent oublié : l’écosystème Rust est cohérent et agréable à vivre :
    • cargo unifie build, test, dépendances, doc, release… sans bricolage.
    • Pas besoin d’aller choisir entre 5 build systems incompatibles.
    • Les crates sont versionnées, documentées, souvent testées. Et l’outil les gère sans que j’aie à lire leur README pour intégrer proprement un code tiers.

Je ne dis pas que Rust est “facile”. Mais il m’a permis de construire des choses complexes — avec multithreading, macros, async, généricité — sans avoir besoin d’être un “expert système”.
Et surtout : j’ai confiance dans ce que je produis, parce que je sais que la complexité est visible, pas silencieuse.
Alors oui, unsafe, c’est vraiment pour les experts. Mais Rust, lui, est pour les développeurs ambitieux mais pas kamikazes.

Citation Envoyé par koala01 Voir le message
A quel point devrez vous effectivement être un expert de Rust pour pouvoir utiliser son mode unsafe de manière "suffisamment sécuritaire" ?
C'est à ce niveau, qu'il faut effectivement investir dans une expertise (hormis les cas simples et usuels clairement référencés dans l'API).
En gros, pour être vraiment compétant dans l'usage général des unsafe, je pense qu'il faudrait bien se familiariser avec le développement de Rust lui-même, bref, avoir une bonne compréhension de la manière dont la librairie standard et le compilateur sont conçus.
Savoir comment les données sont alignées en mémoires (il y a plusieurs layout, pas toujours figés), assurer manuellement les contrats requis dans les blocs unsafe, comprendre en profondeur les invariants du langage...

Par exemple, une difficulté des UB parmi d'autres, c'est que la syntaxe induit des hypothèses utilisées pour l'optimisation de code au niveau de la compilation (qui se fait en 2 temps Rust -> LLVM IR -> natif/Wasm). unsafe débraye certaines sécurités qui peuvent alors induire des incohérences. Il faut vraiment avoir l'expertise pour voir cela. Typiquement, on peut produire du code qui fonctionne de manière différente en mode Debug et en mode Release.
Quand je veux lever un doute, je vais typiquement consulter les sources du std, je consulte l'API si le point est mentionné, le Rustomicon et je pose des questions sur stackoverflow ou un autre site.

En fait, l'unsafe ce n’est pas du Rust “difficile”, c’est du Rust “systémique”.
3  0 
Avatar de vVDB.fr
Membre régulier https://www.developpez.com
Le 13/05/2025 à 13:14
Pour une personne qui 'sur' maîtrise son sujet, oui, Rust est un garde chiourme, c'est évident...
Pour un 'débutant', C++ est un truc de spécialistes où il faut une rigueur intellectuelle très supérieure quand on a pris l'habitude d'être volage entre toutes les sollicitations de son portable 😂
Si vous voulez produire de la qualité, vous avez besoin de plusieurs nounous !
Rust est insuffisant, il n'en fait pas assez.
2  0