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
Opinión de Hacker News
La razón por la que
Pines difícil de entender es que la documentación oficial no lo explica con claridadPingarantiza que un objeto nunca se moverá", pero eso en realidad no es ciertoUnpin, así que normalmentePinno hace nadaTpara los quePinrealmente funciona es muy particular, y la documentación no lo enfatiza lo suficientePines difícil porque por sí mismo no tiene significadoPin, ni el lenguaje ni la biblioteca estándar te dicen qué puede y qué no puede hacerPinInnerTypecrea métodos y APIs adicionales (internamente inseguros) para poder manipular objetos fijadosPinen 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
Pines un buen ejemplo de un nombre técnicamente correcto pero difícil de entenderDroptiene un significado más familiar, pero "pinning" noimmovable!(…)sería mejor, aunque es difícil pensar en un nombre mejorprevent_moving!(…)y un traitPreventMovepodrían ser mejoresSi en un lenguaje similar a Rust existieran move-constructors, la necesidad de
Pinpodría desaparecerSe puede mover un objeto mediante
mem::swap/replacea través de una referencia&mut, pero en la práctica rara vez hace faltaswapyreplacepodría resolver el problemaWithoutBoats está teniendo una discusión muy activa sobre iteradores asíncronos,
pollypinPinning/!Movees útil para muchos casos además de async/await