Avant d’ajouter les nouveaux
Animator à notre code, il faut adresser une grosse difficulté inhérente au problème.
Pour permettre une interaction entre les entités pendant leur animation, la fonction
animate() des deux nouveaux
Animator doit accéder simultanément à l’entité en modification et à l’ensemble des autres entités dont elle fait partie.
Cela implique de conserver à la fois une référence exclusive (mutable) et une référence partagée (non-mutable) vers la même séquence d’éléments.
Bien que cela soit possible dans la majorité des langages de programmation, ce ne l’est pas en Rust.
En effet, même si le programmeur prend toutes les précautions dans son code, le
borrow-checker n’est pas capable à la compilation d’assurer que la même donnée ne sera pas lue et en modifiée au même moment.
Rust n’accepte aucun risque de corruption de données.
La solution consiste à forcer le contrôle de la cohérence de l’accès aux données à l’exécution (et non plus à la compilation car c’est impossible).
En Rust, l’analyse au moment de l’exécution est possible grâce à la notion de l’
exclusivité dynamique (
interior-mutability), proposé à travers le type
std::cell:RefCell
Un emplacement mémoire mutable avec des règles d'accès vérifiées dynamiquement :
https://doc.rust-lang.org/std/cell/struct.RefCell.html
.
L’
interior-mutability propose de n’utiliser que des références partagées (non-mutables) sur les données et de ne demander un accès (exclusif ou partagé) qu’au moment où nous voulons accéder à la donnée.
Si à l’exécution, il se vérifie la coexistence d’une référence exclusive (mutable) avec d’autres références sur la même donnée, une erreur irrécupérable serait signalée avec
panic!().
Procédons alors à la modification de notre code pour bénéficier de la notion d’exclusivité dynamique sur l’ensemble des entités possédées par la fenêtre.
Tout d’abord, le type du membre entities de la classe
Window n’est plus
Vec<Entity> mais
Vec<std::cell::RefCell<Entity>>, qui offre l’
interior-mutability.
Évidemment, partout où nous manipulions un vecteur d’entités, maintenant il faudra manipuler un vecteur de
RefCell d’entités, notamment dans la fonction
add_entity().
Assurez vous d’ajouter au vecteur un
RefCell d'
Entity.
L’accès aux éléments du vecteur entities doit se faire uniquement par références partagées (non-mutables) et vous demanderez un accès explicite exclusif ou partagé au moment précis où il faudra y accéder :
- en consultation par référence partagée (non-mutable) obtenue en invoquant la fonction borrow() du type std::cell::Ref ;
- en modification par référence exclusive (mutable) obtenue en invoquant la fonction borrow_mut() du type std::cell::Ref.
Faites fonctionner votre application, vous devez avoir le même résultat qu’avant.