1 pontos por GN⁺ 2024-07-22 | 1 comentários | Compartilhar no WhatsApp

Pin

  • O tipo Pin e o conceito de pinning são componentes fundamentais do ecossistema assíncrono de Rust
  • No entanto, Pin é um dos elementos mais difíceis de abordar e mais fáceis de interpretar errado
  • Este artigo explica o que Pin busca alcançar, como ele surgiu e quais são os problemas atuais de Pin

Requirements

  • Para dar suporte a referências em funções assíncronas, era necessário armazenar referências dentro de Future
  • O problema é que essas referências podem ser autoreferenciais
  • Código de exemplo:
    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
    }
    
  • O estado interno de Bar é o seguinte:
    enum Bar {
        Start { x: i32, y: i32 },
        FirstAwait { z: i32, foo: Foo<'?> },
        Complete,
    }
    
  • O objetivo de Pin é manipular tipos autoreferenciais com segurança

Non-solutions: move constructors and offset pointers

  • Construtores de movimento e ponteiros com deslocamento não funcionam em Rust
  • Construtores de movimento ajustam ponteiros durante a movimentação, mas isso não é possível em Rust
  • Ponteiros com deslocamento não funcionam porque, em tempo de compilação, não é possível saber se uma referência é autoreferencial ou não

The “pinned typestate”

  • Um objeto não precisa ser imóvel sempre, mas precisa se tornar imóvel a partir de um determinado momento
  • No modelo de Ralf Jung, o objeto faz a transição do estado "possuído" para o estado "compartilhado" e depois para o estado "fixado"
  • Ao entrar no estado fixado, o objeto não pode mais ser movido

?Move

  • Antes de Pin, foi tentada uma solução baseada em um novo trait chamado Move
  • Tipos que não implementam Move passam para o estado fixado quando uma referência é obtida
  • Porém, Move não oferece compatibilidade retroativa

Pin

  • Pin projetou um novo tipo de referência para colocar objetos no estado fixado
  • Pin foi implementado como uma API de biblioteca para manter a compatibilidade retroativa
  • Foi adicionado o auto trait Unpin para que a maioria dos tipos não precise distinguir entre o estado fixado e o estado normal

The problems with Pin

  • Pin tem vários problemas de usabilidade
  • Pin foi implementado como um tipo de biblioteca, e por isso muitas funcionalidades dos tipos normais de referência se perdem
  • Por exemplo, &mut T não implementa Copy, mas pode ser passado como argumento várias vezes
  • Pin não oferece essa conveniência
  • Muito da confusão surge ao usar Pin

In my next post…

  • Pin permite compilar com segurança referências arbitrárias em funções assíncronas
  • No entanto, Pin aumenta a complexidade, e o próximo artigo tratará de formas de melhorar isso

Resumo do GN⁺

  • Pin é um componente importante do ecossistema assíncrono de Rust
  • Os problemas de usabilidade de Pin decorrem do fato de ele ter sido implementado como um tipo de biblioteca
  • O próximo artigo abordará maneiras de melhorar Pin
  • Um projeto com funcionalidade semelhante é pin-project-lite

1 comentários

 
GN⁺ 2024-07-22
Comentários no Hacker News
  • O motivo de Pin ser difícil de entender é que a documentação oficial não o explica com clareza

    • A documentação afirma que "Pin garante que um objeto nunca será movido", mas isso não é verdade
    • Como a maioria dos objetos comuns é Unpin, Pin normalmente não faz nada
    • O conjunto de tipos T para os quais Pin realmente funciona é muito peculiar, e isso não é suficientemente enfatizado na documentação
  • Pin é difícil porque não tem significado por si só

    • No caso de Pin, nem a linguagem nem a biblioteca padrão dizem o que Pin pode ou não pode fazer
    • Em vez disso, o provedor de InnerType cria métodos e APIs adicionais (internamente inseguros) para permitir a manipulação de objetos fixados
    • O único propósito de Pin em si é fornecer um ponteiro com menos "funcionalidades intrínsecas"
  • Deveriam adicionar "rust" ao título para ficar claro sobre o que é o artigo

  • O termo "value identity" não é definido em nenhum lugar da documentação do Mojo

    • Recomendo a palestra de Dave Abrahams, "Value Semantics: Safety, Independence, Projection, & Future of Programming"
  • Pin é um bom exemplo de um nome tecnicamente correto, mas difícil de entender

    • Drop tem um significado mais familiar, mas "pinning" não
    • immovable!(…) talvez fosse melhor, mas é difícil pensar em um nome melhor
    • Um nome descritivo como prevent_moving!(…) e um trait PreventMove poderiam ser melhores
  • Em uma linguagem parecida com Rust que tivesse move-constructors, a necessidade de Pin poderia desaparecer

    • Como o usuário não teria como destruir o objeto, também não teria como movê-lo
  • É possível mover objetos via mem::swap/replace por meio de uma referência &mut, mas isso raramente é realmente necessário

    • Seria bom se houvesse uma forma de optar por referências de movimento
    • Tornar swap e replace inseguros poderia resolver o problema
  • WithoutBoats está promovendo uma discussão ativa sobre os temas iteradores assíncronos, poll e pin

    • Quase não existem comunidades que discutam publicamente em profundidade os detalhes de uma linguagem, e é muito interessante acompanhar isso
  • Pinning/!Move é útil para muitos usos além de async/await

    • Mas Rust não lida com isso adequadamente, então a resposta habitual é "reescreva o programa em outra linguagem"