1 points par GN⁺ 2024-07-22 | 1 commentaires | Partager sur WhatsApp

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

 
GN⁺ 2024-07-22
Discussion sur Hacker News
  • Pin est difficile à comprendre parce que la documentation officielle ne l’explique pas clairement

    • La documentation affirme que « Pin garantit qu’un objet ne sera jamais déplacé », mais ce n’est pas réellement vrai
    • La plupart des objets ordinaires implémentent Unpin, donc Pin ne sert généralement à rien
    • L’ensemble des types T pour lesquels Pin fonctionne réellement est très particulier et cela n’est pas assez mis en avant dans la documentation
  • Pin est difficile parce qu’il n’a pas de signification en soi

    • Dans le cas de Pin, ni le langage ni la bibliothèque standard n’indiquent ce que Pin peut ou ne peut pas faire
    • À la place, le fournisseur de InnerType crée des méthodes et des API supplémentaires (internement unsafe) pour permettre de manipuler les objets épinglés
    • Le seul but de Pin en 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

    • Recommandation de la conférence de Dave Abrahams « Value Semantics: Safety, Independence, Projection, & Future of Programming »
  • Pin est un bon exemple de nom techniquement correct mais difficile à comprendre

    • Drop a 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 nom
    • Un nom descriptif comme prevent_moving!(…) et un trait PreventMove pourraient être préférables
  • Dans un langage similaire à Rust avec des move-constructors, le besoin de Pin pourrait disparaître

    • Comme l’utilisateur n’aurait aucun moyen de détruire l’objet, il n’aurait pas non plus de moyen de le déplacer
  • Il est possible de déplacer un objet via une référence &mut avec mem::swap/replace, mais c’est rarement nécessaire en pratique

    • Il serait souhaitable d’avoir un moyen de choisir des références déplaçables
    • Rendre swap et replace unsafe pourrait résoudre le problème
  • WithoutBoats mène des discussions actives sur les itérateurs asynchrones, poll et pin

    • Il existe peu de communautés qui discutent aussi profondément et publiquement des détails d’un langage, et c’est très intéressant à observer
  • Le pinning/!Move est utile pour bien d’autres usages que l’async/await

    • Mais Rust ne le gère pas correctement, donc la réponse habituelle est « réécrivez le programme dans un autre langage »