1 puntos por GN⁺ 2024-07-22 | 1 comentarios | Compartir por WhatsApp

Pin

  • El tipo Pin y el concepto de pinning son componentes fundamentales del ecosistema asíncrono de Rust
  • Sin embargo, Pin es uno de los elementos más difíciles de abordar y más propensos a malentendidos
  • Este artículo explica qué intenta lograr Pin, cómo surgió y cuáles son sus problemas actuales

Requirements

  • Para soportar referencias en funciones asíncronas, era necesario almacenar referencias dentro de Future
  • El problema es que esas referencias pueden ser autorreferenciales
  • Código de ejemplo:
    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
    }
    
  • El estado interno de Bar es el siguiente:
    enum Bar {
        Start { x: i32, y: i32 },
        FirstAwait { z: i32, foo: Foo<'?> },
        Complete,
    }
    
  • El objetivo de Pin es manipular de forma segura los tipos autorreferenciales

Non-solutions: move constructors and offset pointers

  • Los constructores de movimiento y los punteros con desplazamiento no funcionan en Rust
  • Los constructores de movimiento modifican los punteros al mover, pero eso no es posible en Rust
  • Los punteros con desplazamiento no funcionan porque en tiempo de compilación no se puede saber si una referencia es autorreferencial o no

The “pinned typestate”

  • Un objeto no siempre debe ser inamovible, sino que debe volverse inamovible a partir de cierto momento
  • En el modelo de Ralf Jung, un objeto pasa del estado "poseído" al estado "compartido" y luego al estado "fijado"
  • Una vez que entra en el estado fijado, el objeto ya no puede moverse

?Move

  • Antes de Pin, se intentó una solución basada en un nuevo trait llamado Move
  • Los tipos que no implementan Move pasan al estado fijado al tomar una referencia
  • Sin embargo, Move no ofrece compatibilidad retroactiva

Pin

  • Pin diseña un nuevo tipo de referencia que coloca a un objeto en estado fijado
  • Pin está implementado como una API de biblioteca, lo que preserva la compatibilidad retroactiva
  • Se agregó el auto trait Unpin para que la mayoría de los tipos no distingan entre el estado fijado y el estado normal

The problems with Pin

  • Pin tiene varios problemas de usabilidad
  • Pin está implementado como un tipo de biblioteca, por lo que pierde muchas capacidades que tienen los tipos de referencia normales
  • Por ejemplo, &mut T no implementa Copy, pero puede pasarse como argumento varias veces
  • Pin no ofrece esa comodidad
  • Al usar Pin surge mucha confusión

In my next post…

  • Pin permite compilar de forma segura referencias arbitrarias en funciones asíncronas
  • Sin embargo, Pin incrementa la complejidad, y en el próximo artículo se tratarán formas de mejorarlo

Resumen de GN⁺

  • Pin es un componente importante del ecosistema asíncrono de Rust
  • Los problemas de usabilidad de Pin se deben a que fue implementado como un tipo de biblioteca
  • En el próximo artículo se tratarán formas de mejorar Pin
  • Un proyecto con funcionalidad similar es pin-project-lite

1 comentarios

 
GN⁺ 2024-07-22
Opinión de Hacker News
  • La razón por la que Pin es difícil de entender es que la documentación oficial no lo explica con claridad

    • La documentación afirma que "Pin garantiza que un objeto nunca se moverá", pero eso en realidad no es cierto
    • La mayoría de los objetos comunes son Unpin, así que normalmente Pin no hace nada
    • El conjunto de tipos T para los que Pin realmente funciona es muy particular, y la documentación no lo enfatiza lo suficiente
  • Pin es difícil porque por sí mismo no tiene significado

    • En el caso de Pin, ni el lenguaje ni la biblioteca estándar te dicen qué puede y qué no puede hacer Pin
    • En cambio, quien provee InnerType crea métodos y APIs adicionales (internamente inseguros) para poder manipular objetos fijados
    • El único propósito de Pin en sí es ofrecer un puntero con menos "funcionalidad intrínseca"
  • Habría que agregar "rust" al título para saber de qué trata el artículo

  • El término "value identity" no está definido en ninguna parte de la documentación de Mojo

    • Recomiendan la charla de Dave Abrahams "Value Semantics: Safety, Independence, Projection, & Future of Programming"
  • Pin es un buen ejemplo de un nombre técnicamente correcto pero difícil de entender

    • Drop tiene un significado más familiar, pero "pinning" no
    • Quizás immovable!(…) sería mejor, aunque es difícil pensar en un nombre mejor
    • Un nombre descriptivo como prevent_moving!(…) y un trait PreventMove podrían ser mejores
  • Si en un lenguaje similar a Rust existieran move-constructors, la necesidad de Pin podría desaparecer

    • Como el usuario no tendría forma de destruir el objeto, tampoco tendría forma de moverlo
  • Se puede mover un objeto mediante mem::swap/replace a través de una referencia &mut, pero en la práctica rara vez hace falta

    • Ojalá hubiera una forma de elegir referencias de movimiento
    • Volver inseguros swap y replace podría resolver el problema
  • WithoutBoats está teniendo una discusión muy activa sobre iteradores asíncronos, poll y pin

    • Casi no hay comunidades que discutan públicamente con tanta profundidad los detalles de un lenguaje, y es muy interesante verlo
  • Pinning/!Move es útil para muchos casos además de async/await

    • Pero como Rust no lo maneja bien, la respuesta habitual suele ser "reescribe el programa en otro lenguaje"