Pin
- Le type
Pin et le concept de pinning sont des éléments fondamentaux de l’écosystème asynchrone de Rust
- Pourtant,
Pin est aussi l’un des éléments les plus difficiles d’accès et les plus sujets aux malentendus
- Cet article explique ce que
Pin cherche à accomplir, comment il est apparu et quels sont aujourd’hui ses problèmes
Exigences
- Pour prendre en charge les références dans les fonctions asynchrones, il fallait stocker des références à l’intérieur de
Future
- Le problème est que ces références peuvent être auto-référentielles
- Exemple de code :
async fn foo<'a>(z: &'a mut i32) { ... }
async fn bar(x: i32, y: i32) -> i32 {
let mut z = x + y;
foo(&mut z).await;
z
}
- L’état interne de
Bar est le suivant :
enum Bar {
Start { x: i32, y: i32 },
FirstAwait { z: i32, foo: Foo<'?> },
Complete,
}
- L’objectif de
Pin est de manipuler en toute sécurité des types auto-référentiels
Fausses solutions : constructeurs de déplacement et pointeurs d’offset
- Les constructeurs de déplacement et les pointeurs d’offset ne fonctionnent pas en Rust
- Les constructeurs de déplacement modifient les pointeurs lors d’un déplacement, mais cela est impossible en Rust
- Les pointeurs d’offset ne fonctionnent pas non plus, car il est impossible de savoir à la compilation si une référence est auto-référentielle ou non
Le « typestate épinglé »
- Un objet ne doit pas être immobile en permanence, mais seulement à partir d’un certain moment
- Dans le modèle de Ralf Jung, un objet passe de l’état « possédé » à l’état « partagé », puis à l’état « épinglé »
- Une fois dans l’état épinglé, l’objet ne peut plus être déplacé
?Move
- Avant
Pin, une solution fondée sur un nouveau trait appelé Move a été envisagée
- Les types qui n’implémentent pas
Move passent à l’état épinglé lorsqu’on prend une référence sur eux
- Cependant,
Move n’offre pas de rétrocompatibilité
Pin
Pin introduit un nouveau type de référence conçu pour placer un objet dans l’état épinglé
Pin est implémenté comme une API de bibliothèque afin de préserver la rétrocompatibilité
- L’ajout de l’auto-trait
Unpin permet à la plupart des types de ne pas distinguer l’état épinglé de l’état normal
Les problèmes de Pin
Pin présente plusieurs problèmes d’utilisabilité
Pin étant implémenté comme un type de bibliothèque, il perd de nombreuses capacités des types de référence ordinaires
- Par exemple,
&mut T n’implémente pas Copy, mais peut tout de même être passé plusieurs fois en argument
Pin n’offre pas ce type de confort
- Son utilisation est source de nombreuses confusions
Dans mon prochain billet…
Pin permet de compiler en toute sécurité des références arbitraires dans des fonctions asynchrones
- Mais
Pin augmente aussi la complexité, et le prochain article abordera des pistes pour l’améliorer
Résumé GN⁺
Pin est un composant important de l’écosystème asynchrone de Rust
- Les problèmes d’utilisabilité de
Pin viennent du fait qu’il est implémenté comme un type de bibliothèque
- Le prochain article traitera de moyens d’améliorer
Pin
pin-project-lite est un projet proposant des fonctionnalités similaires
1 commentaires
Discussion sur Hacker News
Pinest difficile à comprendre parce que la documentation officielle ne l’explique pas clairementPingarantit qu’un objet ne sera jamais déplacé », mais ce n’est pas réellement vraiUnpin, doncPinne sert généralement à rienTpour lesquelsPinfonctionne réellement est très particulier et cela n’est pas assez mis en avant dans la documentationPinest difficile parce qu’il n’a pas de signification en soiPin, ni le langage ni la bibliothèque standard n’indiquent ce quePinpeut ou ne peut pas faireInnerTypecrée des méthodes et des API supplémentaires (internementunsafe) pour permettre de manipuler les objets épinglésPinen soi est de fournir un pointeur avec moins de « fonctionnalités intrinsèques »Il faudrait ajouter « rust » au titre pour savoir de quoi parle l’article
Le terme « value identity » n’est défini nulle part dans la documentation de Mojo
Pinest un bon exemple de nom techniquement correct mais difficile à comprendreDropa un sens plus familier, mais ce n’est pas le cas de « pinning »immovable!(…)serait peut-être meilleur, mais il est difficile d’imaginer un meilleur nomprevent_moving!(…)et un traitPreventMovepourraient être préférablesDans un langage similaire à Rust avec des move-constructors, le besoin de
Pinpourrait disparaîtreIl est possible de déplacer un objet via une référence
&mutavecmem::swap/replace, mais c’est rarement nécessaire en pratiqueswapetreplaceunsafepourrait résoudre le problèmeWithoutBoats mène des discussions actives sur les itérateurs asynchrones,
polletpinLe pinning/
!Moveest utile pour bien d’autres usages que l’async/await