possible d'obtenir le modèle de propriété de Rust avec un wrapper C ++ générique?

15

En parcourant cet article sur la sécurité d'accès simultané de Rust:

http://blog.rust-lang.org/2015/04/10/Fearless-Concurrency.html

Je me demandais combien de ces idées peuvent être réalisées en C ++ 11 (ou plus récent). En particulier, puis-je créer une classe propriétaire qui transfère la propriété à n'importe quelle méthode à laquelle elle peut être transmise? Il semble que C ++ ait tellement de façons de transmettre des variables que ce serait impossible, mais peut-être pourrais-je mettre des restrictions sur la classe ou le modèle pour m'assurer que du code de modèle est exécuté à chaque passage de méthode?

Brannon
la source
Quelques citations du lien amélioreraient cette question
Martin Ba
2
@delnan (Safe) Rust garantit que vous n'avez jamais plus d'une référence mutable à quelque chose à la fois et que vous n'avez jamais de référence mutable à une chose à laquelle vous avez également des références en lecture seule. Il a également certaines restrictions sur le transfert de données entre les threads. Ensemble, ils empêchent une classe importante de bogues liés au threading et facilitent le raisonnement sur l'état des objets, même dans le code à thread unique.
CodesInChaos
3
Vous ne pensez pas que vous pouvez exprimer l'emprunt d'une manière que le compilateur C ++ pourrait vérifier, vous devrez donc recourir à l'application de l'exécution avec le hit de performance associé.
CodesInChaos
1
Les propriétés de portée ne sont-elles pas déjà implémentées par des pointeurs intelligents en C ++ 11?
Akshat Mahajan
1
@JerryJeremiah Rust possède une grande variété de types de référence. Les basiques,, &ne nécessitent aucune promotion pour être utilisés. Si vous essayez d'obtenir un &mutmoment où vous avez encore une autre référence (modifiable ou non) au même élément, vous ne pourrez pas compiler. RefCell<T>déplace la vérification à l'exécution, vous aurez donc une panique si vous essayez .borrow_mut()quelque chose qui a déjà un actif .borrow()ou .borrow_mut(). Rust a aussi Rc<T>(pointeur de propriété partagé) et son frère Weak<T>, mais il s'agit de propriété, pas de mutabilité. Collez-y un RefCell<T>pour la mutabilité.
8bittree

Réponses:

8

C ++ a trois façons de passer des paramètres à une fonction: par valeur, par référence lvalue et par référence rvalue. Parmi ceux-ci, le passage par la valeur crée la propriété dans le sens où la fonction appelée reçoit sa propre copie, et le passage par la référence rvalue indique que la valeur peut être consommée, c'est-à-dire qu'elle ne sera plus utilisée par l'appelant. Le passage par la référence lvalue signifie que l'objet est temporairement emprunté à l'appelant.

Cependant, ceux-ci ont tendance à être «par convention» et ne peuvent pas toujours être vérifiés par le compilateur. Et vous pouvez accidentellement transformer une référence lvalue en une référence rvalue à l'aide de std::move(). Concrètement, il y a trois problèmes:

  • Une référence peut survivre à l'objet auquel elle fait référence. Le système à vie de Rust empêche cela.

  • Il peut y avoir plus d'une référence mutable / non const active à tout moment. Le vérificateur d'emprunt de Rust empêche cela.

  • Vous ne pouvez pas refuser les références. Vous ne pouvez pas voir sur un site d'appel si cette fonction crée une référence à votre objet, sans connaître la signature de la fonction appelée. Par conséquent, vous ne pouvez pas empêcher de manière fiable les références, ni en supprimant les méthodes spéciales de vos classes ni en vérifiant le site d'appel pour vérifier la conformité avec certains guides de style «sans références».

Le problème de durée de vie concerne la sécurité de base de la mémoire. Il est bien sûr illégal d'utiliser une référence lorsque l'objet référencé a expiré. Mais il est très facile d'oublier la durée de vie lorsque vous stockez une référence dans un objet, en particulier lorsque cet objet dépasse la portée actuelle. Le système de type C ++ ne peut pas tenir compte de cela car il ne modélise pas du tout la durée de vie des objets.

Le std::weak_ptrpointeur intelligent code la sémantique de propriété similaire à une simple référence, mais nécessite que l'objet référencé soit géré via un shared_ptr, c'est-à-dire qu'il soit compté par référence. Ce n'est pas une abstraction à coût nul.

Alors que C ++ a un système const, cela ne suit pas si un objet peut être modifié, mais suit si un objet peut être modifié via cette référence particulière . Cela ne fournit pas de garanties suffisantes pour une «concurrence courageuse». En revanche, Rust garantit que s'il y a une référence mutable active qui est la seule référence ("Je suis le seul qui peut changer cet objet") et s'il y a des références non mutables alors toutes les références à l'objet sont non mutables ("Alors que je peux lire à partir de l'objet, personne ne peut le changer").

En C ++, vous pourriez être tenté de protéger l'accès à un objet via un pointeur intelligent avec un mutex. Mais comme discuté ci-dessus, une fois que nous avons une référence, il peut échapper à sa durée de vie attendue. Un tel pointeur intelligent ne peut donc pas garantir qu'il s'agit du point d'accès unique à son objet géré. Un tel schéma peut en fait fonctionner dans la pratique parce que la plupart des programmeurs ne veulent pas se saboter, mais d'un point de vue système de type, cela est encore complètement malsain.

Le problème général avec les pointeurs intelligents est qu'ils sont des bibliothèques en plus du langage principal. L'ensemble des fonctionnalités du langage de base permet à ces pointeurs intelligents, par exemple std::unique_ptrbesoin de constructeurs de mouvement. Mais ils ne peuvent pas corriger les lacunes du langage principal. Les capacités de créer implicitement des références lors de l'appel d'une fonction et d'avoir des références pendantes signifient que le langage C ++ de base n'est pas sain. L'incapacité de limiter les références mutables à une seule signifie que C ++ ne peut garantir la sécurité contre les conditions de concurrence avec tout type de concurrence.

Bien sûr, à bien des égards, C ++ et Rust se ressemblent plus qu'ils ne se ressemblent, en particulier en ce qui concerne leurs concepts de durée de vie d'objets statiquement déterminées. Mais bien qu'il soit possible d'écrire des programmes C ++ corrects (à condition qu'aucun des programmeurs ne fasse d'erreur), Rust garantit l' exactitude des propriétés discutées.

amon
la source
Si le problème est que C ++ ne suit pas la propriété dans le langage principal, serait-il possible d'implémenter cette fonctionnalité via la méta-programmation? Cela signifie que vous créeriez une nouvelle classe de pointeur intelligent qui serait sans danger pour la mémoire en (1) la forçant à pointer exclusivement vers des objets qui n'utilisent que des pointeurs intelligents de la même classe et (2) en suivant la propriété via des modèles
Elliot Gorokhovsky
2
@ElliotGorokhovsky Non, car le modèle ne peut pas désactiver les fonctionnalités de base du langage telles que les références. Un pointeur intelligent peut rendre plus difficile l'obtention d'une référence, mais à ce stade, vous vous battez contre le langage - la plupart des fonctions de bibliothèque standard ont besoin de références. Il n'est pas non plus possible de vérifier la durée de vie d'une référence via des modèles car le langage n'offre aucun concept réifié de durée de vie.
amon
Je vois, merci
Elliot Gorokhovsky