Bonjour,
Je m'entraîne depuis plusieurs semaines en Rust. Je bloque (comme tous les nouveaux j'imagine), sur la question de l'emprunt.
Voici un code basique que j'ai écrit. Il reprend d'événements qui font un appel à des fonctions de rappel ("sauce Javascript"). En soi le code compile et fonctionne...
Code Rust : Sélectionner tout - Visualiser dans une fenêtre à part
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 use std::collections::HashMap; #[allow(dead_code)] #[derive(Debug,Copy,Clone,PartialEq,Eq,Hash)] enum EvenementType{ Load, Unload } // enum EvenementValeur{} #[derive(Debug)] struct Evenement<T> { evt_type: EvenementType, evt_valeur: T } struct Objet<T> { valeur: T, evt_listeners: HashMap<EvenementType,Vec<fn(&mut Objet<T>, &Evenement<T>)>> } impl<T> Objet<T> { fn add_event_listener( &mut self, evt_type: EvenementType, evt_rappel: fn(&mut Objet<T>, &Evenement<T>) ) { if !self.evt_listeners.contains_key( &evt_type ) { self.evt_listeners.insert( evt_type.clone(), vec!() ); } if let Some( vecteur ) = self.evt_listeners.get_mut( &evt_type ) { vecteur.push( evt_rappel ); } } fn raise_event( &mut self, evt: &Evenement<T> ) { // <----------- fonction bien pensée ?? let mut vecteur: Option<Vec<fn(&mut Objet<T>, &Evenement<T>)>> = None; if let Option::Some( v ) = &self.evt_listeners.get( &evt.evt_type ) { vecteur = Option::Some( v.to_vec() ); // <------------- revient à cloner le vecteur... } if let Some( v ) = vecteur { for rappel in v { rappel( self, evt ); } } } } fn main() { let mut o = Objet::<usize> { valeur: 0, evt_listeners: HashMap::new() }; println!("v initiale : {:?}", o.valeur); o.add_event_listener( EvenementType::Load, |obj:&mut Objet<usize>, evt: &Evenement<usize>| { obj.valeur = evt.evt_valeur; } ); let e = Evenement { evt_type: EvenementType::Load, evt_valeur: 1 }; o.raise_event( &e ); println!("v finale : {:?}", o.valeur); }
Ma fonction "raise_event" me pose soucis. Elle prend en argument un événement, partagé de manière immuable (non-mutable) ainsi que l'objet visé via une référence altérable (mutable). La fonction va chercher dans un HashMap si le type d'événement est connu et si oui, fait appel à des fonctions stockées comme valeurs d'un vecteur, lui-même stocké comme valeur du HashMap.
Problème : l'accès au HashMap impose un emprunt de 'self' ce qui me bloque ensuite un nouvel emprunt altérable de 'self' pour la fonction de rappel qui est appelée (ce qui ne compile évidemment pas).
La solution la plus "élégante" que j'ai trouvé, est de faire un clonage du vecteur des fonctions de rappel. J'ai bien pensé à implémenter le trait Copy, mais ça me semble impossible dans cette situation (j'ai tort ?).
Du coup j'ai en mémoire deux fois le vecteur (l'un dans le HashMap, l'autre pour l'utilisation immédiate dans le bloc suivant). Certes en soi ce doublement n'est pas long, le clone étant rapidement retirée de la mémoire, mais l'idéal serait de ne pas faire ce fichu clonage... et donc de contourner élégamment la limitation d'emprunt.
Qu'en pensez-vous ? Là je sèche...
Merci à tous,
Julien.
Partager