3 pontos por GN⁺ 2023-10-26 | 1 comentários | Compartilhar no WhatsApp
  • Depois de migrar de JavaScript para Rust para focar em WebAssembly, o autor avaliou o valor real de Rust ao longo de 3 anos criando o Wick, implantações em produção, um ebook e cerca de 100 pacotes no crates.io
  • O borrow checker, o sistema de tipos rico, padrões funcionais e a ausência de null evitam muitos erros em tempo de compilação e permitem manter uma base de código grande com menos testes
  • Clippy e Cargo workspace são poderosos, mas lacunas em ferramentas e no ecossistema, como configuração global de lint e publicação de workspaces, acabam gerando custo operacional
  • Async, refatoração e o gerenciamento de generic·lifetime·trait constraints continuam sendo áreas com mais atrito do que em JavaScript ou Go
  • Rust é robusta e versátil, mas os custos de contratação, aprendizado, iteração rápida e rastreamento de problemas são altos, então faz mais sentido quando o escopo está bem definido ou quando é possível arcar com o custo inicial

WebAssembly levou à escolha de Rust

  • Há alguns anos, o autor deixou de lado o trabalho que fazia para se dedicar 100% a WebAssembly, e naquela época Rust oferecia o melhor suporte para compilação em WebAssembly
  • Os runtimes de WebAssembly mais completos também eram baseados em Rust, então, entre as opções disponíveis, Rust era a alternativa mais realista
  • Depois disso, ele criou o Wick, um framework e runtime de aplicações que usa WebAssembly como sistema central de módulos
  • Ao longo de 3 anos, acumulou experiência com Rust passando por várias implantações em produção, um ebook e o envio de cerca de 100 pacotes para o crates.io

Mantendo mais código com menos testes

  • Em Rust, ele começou escrevendo testes como faria em linguagens comuns, mas percebeu que estava criando testes que não poderiam falhar se o código já compilasse
  • Ao evitar blocos unsafe {} e métodos propensos a panic, como .unwrap(), muitos problemas acabam sendo evitados por padrão
  • O borrow checker, o sistema de tipos rico, os padrões e bibliotecas funcionais, e a ausência de valores null reduzem o esforço necessário com testes
  • No projeto Wick, ele manteve mais de 70.000 linhas de código com muito menos testes do que precisaria em outras linguagens
  • Quando testes são necessários, o harness de testes integrados do Rust facilita adicioná-los ao lado do código

Rust também mudou hábitos de programação em outras linguagens

  • O compilador de Rust continuava reclamando de códigos que em outras linguagens ele considerava normais, e esse processo acabou mudando seus hábitos de programação
  • Agora, mesmo em outras linguagens, ele se incomoda quando a ordem das linhas de código parece estranha ou quando um valor de retorno não é verificado
  • Também passou a ter uma aversão muito maior a erros em tempo de execução
  • O rigor de Rust é incômodo, mas, depois que se acostuma com a proteção oferecida pelo compilador, fica difícil voltar para outras linguagens

Clippy é útil além de ser um linter

  • O Clippy é o linter de Rust, mas funciona mais como uma ferramenta auxiliar amigável que sugere código alternativo do que como um simples verificador
  • A biblioteca padrão de Rust é muito grande, e as funcionalidades estão espalhadas entre tipos, traits, macros e funções, o que dificulta encontrar a API necessária
  • Várias regras detectam padrões comuns que podem ser substituídos de forma melhor por métodos ou tipos da biblioteca padrão
  • centenas de regras cobrindo desempenho, legibilidade e indireções desnecessárias, e muitas também oferecem código alternativo quando possível
  • A configuração global de lint por projeto parecia que seria viabilizada por uma issue do Cargo, mas até lá o Wick precisou atualizar automaticamente, via script, configurações inline de lint em dezenas de crates

O ecossistema tem lacunas que precisam ser aceitas

  • O problema da configuração global do Clippy é apenas um exemplo das lacunas do ecossistema que aparecem com frequência nas ferramentas e bibliotecas de Rust
  • As issues relacionadas já foram fechadas, mas ficaram abertas por anos e demoraram muito para serem resolvidas
  • Rust atrai novos usuários há muito tempo, a ponto de ser eleita repetidamente como “a linguagem mais querida”, mas esse fluxo não se traduziu diretamente em melhorias drásticas em bibliotecas e ferramentas
  • Era comum surgirem forks pontuais para atender casos de uso específicos, e no Wick também houve tentativas de enviar PRs que esbarraram em situações parecidas
  • Entre as possíveis razões estão a pressão por manter APIs estáveis e a granularidade do sistema de tipos
    • Para quem mantém bibliotecas, até mudanças pequenas podem acabar exigindo mudança de versão principal, o que dificulta aceitá-las
    • Também é grande o peso de escrever código Rust que satisfaça as necessidades de todo mundo

Cargo, crates.io e o atrito na publicação de workspaces

  • A estrutura do repositório do Wick foi criada com base em projetos populares e, no começo, parecia razoável, mas os problemas apareceram na etapa de publicação
  • Com Cargo, é fácil compilar, testar e usar crates em tamanho modular, mas publicar no crates.io é outra história
  • No crates.io, todos os crates referenciados precisam estar publicados individualmente para que um pacote possa ser publicado
    • Faz sentido impedir a publicação de crates que dependam de pacotes que existam apenas no sistema de arquivos local
    • Mas, em uma estrutura natural de projeto grande dividido em pequenos módulos internos, não é possível publicar incluindo sub-crates que existam apenas dentro do crate pai
  • Houve uma correção informando que crates com dependências locais de desenvolvimento ainda podem ser publicados se version não estiver incluído no Cargo.toml
  • O suporte a workspaces do Cargo em si é excelente, e a experiência de gerenciar projetos grandes é melhor do que na maioria das linguagens
  • Porém, o workspace não resolve o problema de publicação, e, mesmo havendo várias formas de configuração, é difícil encontrar uma “resposta certa” que publique com facilidade
  • A existência de muitos crates utilitários relacionados a cargo workspace publish já mostra o problema
  • Ao publicar o Wick, era comum gastar mais de 1 hora combinando tarefas manuais repetitivas com ferramentas que funcionavam só parcialmente

Async é uma das maiores fontes de atrito

  • O async em Rust parece um recurso adicionado depois que a linguagem já estava pronta, e, no uso real, frequentemente também passa essa sensação de remendo tardio
  • É difícil entender e resolver os erros, e, ao procurar soluções, ainda é preciso filtrar por múltiplos runtimes e suas abordagens próprias de async
  • Algumas bibliotecas async talvez nem possam ser usadas fora de um runtime async específico
  • Para alguém com 20 anos de JavaScript e experiência com Go, o async de Rust foi a maior fonte de frustração e atrito
  • Não é um problema impossível de superar, mas é preciso estar sempre preparado para o surgimento de problemas com async
  • Em outras linguagens, o async costuma funcionar de maneira tão natural que quase nem aparece

Refatorar pode virar um trabalho pesado

  • O sistema de tipos rico de Rust é tanto vantagem quanto desvantagem
  • Pensar com os tipos de Rust é bom, mas gerenciar os tipos de Rust pode virar um pesadelo
  • Assinaturas de dados e funções podem incluir generic types, generic lifetimes e trait constraints
  • As próprias constraints também podem ter generic types e lifetimes, então às vezes há mais constraints de tipo do que código real
  • É preciso definir generics em cada impl, então isso já é trabalhoso na primeira escrita e, durante refatorações, pequenas mudanças podem se transformar em cascatas de ajustes
  • Quando a mesma lista de constraints ou generics precisa ser repetida em vários lugares, não há um recurso da linguagem ou da ferramenta para criar alias disso ou referenciar uma definição central, então o peso da duplicação permanece

Veredito final: poderosa, mas cara

  • Rust é versátil a ponto de permitir escrever código de nível de sistema, apps CLI, servidores web e clientes web na mesma linguagem
  • Com WebAssembly, é possível executar o mesmo binário de um LLM no navegador e na linha de comando
  • Programas em Rust podem ser extremamente robustos, e, quando se sente na prática os problemas que Rust evita, fica difícil voltar para outras linguagens
  • Quando voltou temporariamente para Go, a velocidade de desenvolvimento voltou a parecer atraente, mas, depois de enfrentar um panic em tempo de execução, essa vantagem perdeu força
  • Rust tem desvantagens claras
    • É difícil contratar
    • O aprendizado é lento
    • É rígida demais para iteração rápida
    • É especialmente difícil rastrear problemas de memória e desempenho, principalmente em código async
    • Nem todas as bibliotecas são boas o suficiente para código seguro
    • As ferramentas de desenvolvimento ainda têm bastante espaço para melhorar
  • Foi possível fazer coisas impressionantes com um time pequeno, mas também houve grandes obstáculos, e, como havia razões técnicas claras para Rust ser mais adequada ao Wick, o autor considera que ainda é cedo para dizer se Rust realmente valeu a pena nesse caso
  • Se for necessário iterar rapidamente, é muito provável que Rust não seja a escolha ideal
  • Se o escopo for conhecido ou se houver condições de absorver um custo inicial maior, Rust merece ser considerada seriamente
  • À medida que a perspectiva de WebAssembly ganha força a cada mês, a possibilidade de reutilizar em muitos lugares um software robusto escrito uma vez só parece cada vez mais próxima da realidade

1 comentários

 
GN⁺ 2023-10-26
Opiniões no Hacker News
  • Usei bastante Rust, mas mesmo depois de alguns anos ainda sinto que a produtividade é baixa
    Hoje em dia uso bastante Zig e sinto que sou umas 10 vezes mais produtivo, porque posso me concentrar só no código que quero escrever e não preciso ficar pensando em quais ferramentas ou bibliotecas usar
    Sei que Rust oferece segurança de memória e que isso é importante, mas a usabilidade é realmente ruim. Toda vez que uso Rust, sinto que estou sendo limitado, e como sempre preciso procurar bibliotecas ou pesquisar como fazer as coisas, não consigo simplesmente “digitar código”
    O sistema de tipos também pode crescer de forma descontrolada, e muitas vezes é difícil saber quais métodos podem de fato ser chamados em uma determinada struct. Rust é uma ferramenta excelente e resolve muitos problemas, mas não acho que seja uma boa linguagem de uso geral

    • Essa sensação não é comum a todo mundo. Para qualquer tarefa um pouco mais complexa que um script de shell, uso Rust, e até controlo meu gerenciador de janelas com um programa em Rust
      Falando como alguém que usou Python por quase 20 anos, hoje trabalho em Rust tão rápido quanto em Python
    • Concordo. Neste momento, sinto que sou muito mais produtivo em C e C++ do que em Rust
      Para mim, Rust parece ter errado completamente o ponto de equilíbrio adequado. É exigente demais com detalhes de baixo nível para escrever aplicações de alto nível, e complexa demais para embedded ou sistemas operacionais
      Para o primeiro caso, eu escolheria C++, Java, Haskell, OCaml, ou até Go com um pouco de C; para o segundo, C usado quase como macro assembly é muito mais adequado
      Ainda tenho a sensação de que a ideia original de Graydon Hoare — algo mais próximo de OCaml/SML, com tipos lineares, garbage collection, alocação na stack, green threads e CPS — teria se tornado uma linguagem muito melhor
    • Minha experiência é exatamente o oposto. Ao usar Rust em sistemas embarcados, minha confiança e velocidade melhoraram muito
      Em C, um pequeno erro frequentemente leva a comportamento indefinido e dores de cabeça; em Rust isso não existe, e isso muda completamente o jogo
    • Fico curioso sobre que tipo de código você escreve. É de nível muito baixo ou de nível muito alto?
      Também queria entender se, em Zig, você quer dizer que não precisa procurar bibliotecas nem descobrir como fazer as coisas
    • Concordo com a sensação geral, mas é difícil colocá-la em palavras
      Rust obriga você a decidir antecipadamente para onde cada bit e byte vai, em qual thread será usado e sob que forma de mutação será tratado. A menos que seja no nível de parsers ou microcontroladores, esse processo parece tedioso
      Gosto de primeiro fazer algo funcionar e depois decidir a melhor estrutura de API, mas Rust entra em conflito com esse processo
      Embora o sistema de tipos de Rust seja mais poderoso, com Swift também dá para obter 90% do desempenho, e o fluxo é muito mais natural
  • A falta de namespaces no crates.io talvez seja o maior ponto de crítica
    Qualquer pessoa pode reservar nomes globais e genéricos de pacotes, e, a menos que se evite o repositório crates.io, a maioria precisa aceitar isso. Só que alguns desses pacotes com nomes genéricos reservados nem são, na prática, os melhores para usar
    Talvez tenha sido uma reação à notação DNS reversa ao estilo Java, que é verbosa e irritante, mas um meio-termo bom teria sido prefixar o nome do pacote com um namespace de usuário/grupo, como no GitHub

    • Fiz uma análise para encontrar ocupadores de nomes no crates.io, e calculei que o principal ocupador criava crates a uma taxa de cerca de um a cada 30 segundos durante uma semana inteira
      Enviei essa análise para a equipe do crates.io e também apontei que existe uma política contra automação, mas eles responderam que isso não era prova suficiente de ocupação de nomes
      O problema do crates.io é que, mesmo quando há uma política clara, ela não é aplicada. Por isso, nomes curtos e fáceis de lembrar para crates já foram todos tomados, e não há como recuperá-los
    • Em vez de uma reação ao DNS reverso ao estilo Java, parece mais que eles seguiram a convenção estabelecida pela maioria dos gerenciadores de pacotes da época
      NPM, PyPI, RubyGems, Hex do Elixir, Cabal do Haskell etc.; por volta de 2014–2015, quando Rust surgiu, não me lembro bem de gerenciadores de pacotes que não fossem Java e que não tivessem um namespace global único
      Alguns tentaram corrigir isso depois, mas na época era simplesmente assim que os gerenciadores de pacotes funcionavam
    • Maven e Java não recebem crédito suficiente pelo fato de o gerenciamento de dependências funcionar bem
      Os sistemas de gerenciamento de dependências piores que surgiram depois em outras linguagens aprenderam muito pouco com os exemplos anteriores
    • Usar URL para pacotes faz bastante sentido. Funciona bem no ecossistema Go
      Também tem a vantagem de eliminar a necessidade de um banco de dados global de pacotes no nível da linguagem. Se você publica um pacote em example.com/your-thing, ele já está lançado
      Claro, se quiser, caches e motores de busca podem ser oferecidos separadamente
    • Não uso Rust com frequência, mas esse tipo de problema é realmente irritante em repositórios de pacotes em geral
      É algo como: http-server é ruim, então não use, use MuffinTop — e você simplesmente precisa saber disso
      A ideia de um nome de pacote oficial é interessante, mas, se com o tempo o código por trás do alias mudar, na prática isso pode ficar confuso
      No fim, parece que isso continuará sendo parte do processo de se tornar especialista de domínio em qualquer ecossistema
  • Se você criar .cargo/config.toml na raiz do workspace, ele se aplica a todos os crates, então dá para configurar lints globais do Clippy
    Dentro do arquivo, basta colocar algo como rustflags = ["-Wclippy::lint_name_to_warn", "-Dclippy::lint_name_to_deny"] sob [build]
    Mas rustflags não é aditivo; ele sobrescreve. Portanto, se houver outra fonte, como a variável de ambiente RUSTFLAGS, ela sobrescreverá essa configuração

    • Nós rodamos um script no momento do commit que adiciona lints aos arquivos lib.rs ou main.rs. É simples
  • Estou aprendendo Rust porque parece claro que ele vai se tornar profissionalmente importante. Quero muito gostar da linguagem e vejo suas vantagens, mas ela está entre as mais desagradáveis que já usei até agora
    Continuei esperando que, com mais proficiência, a antipatia desaparecesse, mas, quanto mais avanço na curva de aprendizado, não é que eu crie muito carinho por ela
    Tudo bem. Provavelmente não será a única linguagem que eu domino e ainda assim não gosto. Só que há tanta gente dizendo que ama Rust que achei que eu também fosse curtir

    • Como contraponto, eu gosto de programar em Rust. A fase de brigar com o borrow checker ficou para trás há muito tempo e, hoje em dia, erros são raros, exceto quando os provoco de propósito para verificar tipos
      O compilador Rust também parece ter melhorado para aceitar uma gama mais ampla de casos válidos
      A chave para entender o borrow checker foi compreender o modelo de memória subjacente. O modelo de memória do Rust é igual ao do C, exceto por extensões para abstrações como genéricos
      As regras do borrow checker parecem arbitrárias no começo, mas estão profundamente conectadas a esse modelo de memória. O verdadeiro valor aparece quando você esbarra no borrow checker sem querer, porque isso indica um bug criado por uma falta de atenção
      O assustador é que, em linguagens como C ou C++, esse código poderia simplesmente ser aceito e seguir adiante
      O sistema de tipos rigoroso e o borrow checker do Rust empurram você suavemente para estruturar o código corretamente, e tenho certeza de que melhoraram meu design de código em todas as linguagens que uso
    • Tentei aprender Rust várias vezes, mas continuo sendo repelido. Simplesmente parece ter usabilidade ruim
    • O que ainda me incomoda é a ausência de valores padrão/argumentos nomeados em funções
      É um recurso bem básico de usabilidade para programadores, compartilhado pela maioria das linguagens populares, e até C++ tem argumentos padrão há muito tempo
    • Fico curioso para saber do que você não gosta
  • Sou um ex-programador C/C++ que chamava pthread quase toda semana durante 10 anos, e hoje uso Rust assíncrono em todo lugar
    Não sei por que async recebe tanto ódio. Na minha opinião, todo mundo deveria usar async para tudo. Incluindo até tarefas “simples” que, por fora, parecem single-thread

    • Acho que o problema é a contagiosidade. Especialmente em ambientes embarcados ou Wasm, o async dominante pode não ser o async que eu quero
      Se o caso de uso do autor fosse Wasm, certamente ele teria outra perspectiva
      Trabalhos que usam buffers grandes ou os reutilizam para evitar custo de alocação muitas vezes também se beneficiam de pools de threads à moda antiga. Dá para resolver isso até certo ponto com bump allocators ou allocators com shards, mas, em loops apertados vetorizáveis e limitados por CPU, pools de threads funcionam melhor
      Async é uma boa ferramenta, mas não existe apenas o contexto em que ela é ideal
    • Recentemente usei um pouco de Rust assíncrono para comparar como o tratamento de erros em chamadas async aninhadas difere do Go, mas até o exemplo trivial que eu queria escrever parecia impossível sem Tokio
      Go, C# e TypeScript não têm essa barreira
      Por exemplo, sem o decorator importado do Tokio, parece que nem dá para usar await dentro da função main
    • O problema em que estou esbarrando agora é que alguns itens, como o Engine do rhai, não são Send, e estou tentando usá-los dentro de uma closure assíncrona
      O GPT-4 sugeriu criar um tokio Runtime dentro de uma thread e usar block_on(). Vou tentar amanhã. Este é meu primeiro projeto Rust de verdade
    • Eu aprendi o oposto. Programadores tendem a transformar tudo em async desnecessariamente, mesmo quando não precisam, aumentando a complexidade e a carga mental
    • Você poderia explicar em mais detalhes? Fico curioso sobre quais seriam exemplos de tarefas simples, aparentemente single-thread, em que valeria escolher async
      Também fico curioso se “agora uso Rust assíncrono em todo lugar” significa “uso Tokio em todo lugar”
  • Programar em Rust realmente não se parece com uma relação abusiva. O compilador tenta ajudar o máximo possível, e em especial as mensagens de erro do rustc estão entre as melhores do mundo

    • O sistema operacional espera que o programador lide corretamente com recursos, e o compilador Rust torna isso muito fácil
      O que mais se aproxima de uma relação abusiva não é o compilador Rust, mas o sistema operacional. O hardware também precisa executar assembly corretamente, então, visto desse jeito, também poderia ser chamado de relação abusiva
      As mensagens de erro do Rust são únicas, e não há compilador que chegue perto
      Além disso, usei LaTeX recentemente e as mensagens de erro eram horríveis. Foi um pesadelo ficar olhando os erros para descobrir o que estava errado
    • No geral é bom, mas eu realmente odeio quando qualquer erro dentro de uma função assíncrona faz surgir, em todos os pontos de chamada recursiva, um erro dizendo que o Future não é mais Send e Sync
      O console inteiro fica coberto de erros, e o erro de sintaxe real fica enterrado em algum lugar no meio
    • O compilador Rust foi o primeiro que vi usar a palavra “perhaps”
      Minha principal reclamação é que ainda não entendo completamente lifetimes, e o compilador nem sempre consegue ajudar. Dá para entender, porque o compilador julga de forma conservadora
  • Em testes em C++, costuma-se dizer que “se compila, provavelmente está certo”
    Se você acha que, como Rust lida com muitos erros, casos de teste comuns se tornam sem sentido, vejo isso como sinal de que, em outras linguagens, você não estava testando o que era correto
    O que deve ser testado não são os problemas da linguagem em si, mas a lógica de negócio
    Se, ao olhar para um código de teste, você sente “em JavaScript eu testaria, mas em Rust não preciso”, então basta apagar esse teste

    • Já ouvi “se compila, provavelmente está certo” em Haskell e Rust, mas nunca ouvi isso sendo aplicado a C++
    • Exceções de ponteiro nulo são bugs que quebram a lógica de negócio
      Não existe essa distinção entre “lógica de negócio versus problema da linguagem”. A linguagem é a base sobre a qual a lógica de negócio fica
      Se você não testa modos de falha, não sei qual é o sentido dos testes
    • Há algo estranhamente impressionante no tipo de programador C++ que considera que um código lixo quebrado compilou, então provavelmente deve estar certo
      Ao contrário da maioria das linguagens conhecidas, C++ tem IFNDR, chamado em tom de piada de falso positivo para a pergunta “isto é um programa C++?”
      Um compilador C++ em conformidade com o padrão é proibido, em alguns casos, de avisar que suspeita que o código escrito não faz sentido; ele deve simplesmente continuar e emitir alguma coisa
      Pode ser um executável que funciona, ou um executável que causa uma catástrofe toda sexta-feira. Não há como saber
      O padrão ISO até identifica esses casos, mas é vago demais para delimitar exatamente o que entra nisso, e meu palpite é que hoje a maior parte dos softwares C++ não triviais é, na prática, IFNDR. É melhor simplesmente rejeitar a linguagem inteira
  • Rust parece ter finalmente quebrado a ideia de que o programador precisa controlar e ter consciência total de tudo o que o compilador faz
    Na verdade, isso já não era verdade havia décadas, e compiladores eram quase mágicos. Com empréstimos, Rust reverteu bastante esse fluxo, fazendo as pessoas se sentirem confortáveis com a ideia de que o compilador sabe melhor do que elas
    Gostaria que isso fosse ainda mais longe. Se não for algo necessário para o algoritmo, não deveríamos precisar iterar explicitamente uma coleção do começo ao fim. Várias operações deveriam ser paralelizadas implicitamente. Seria bom ter um Bash ao estilo Rust

    • Minha experiência com Rust é exatamente o oposto
      Rust é bastante transparente sobre o que faz e é muito conservador em relação a mágica do compilador. A linguagem não faz alocação no heap, não faz contagem de referências e não tem conversões implícitas de tipos numéricos
      Tipos que não foram declarados como copiáveis implicitamente não são copiados, e mesmo isso só é legal para tipos que podem ser copiados por um simples memcpy raso
      Rust usa abstrações de custo zero por toda parte, então é previsível em que código algo será compilado e, em geral, é simples. O layout básico dos tipos padrão também é bem conhecido, então dá para saber que percorrer um Vec será compilado para um loop que incrementa um ponteiro, e não há paralelismo implícito
      Descrever empréstimos como “o compilador saber melhor que o programador” é estranho. Empréstimos são parecidos com verificação de tipos. Se você declara que certo tipo é temporário e tenta usá-lo como se tivesse vida longa, dá erro
      É como uma função declarada para retornar uma struct Foo retornar um Bar: dá erro. O compilador “sabe melhor” simplesmente porque você escreveu um bug
      Empréstimos também compilam para uso direto de ponteiros sem garbage collection e, em structs e funções com ABI C, são garantidamente idênticos a ponteiros C em sentido literal. Se você acha que sabe melhor que o compilador, também pode contornar lifetimes com unsafe
    • Já existem crates que oferecem iteradores paralelos. Basta trocar o nome da chamada de iter()
      Mas não concordo que isso deva ser implícito
    • Dizer que “não se controla o que o compilador faz” é um pouco exagerado
      Em certos aspectos, Rust dá mais controle ao programador do que C. Por exemplo, Rust oferece suporte padrão a assembly inline, enquanto o assembly inline em C depende de extensões específicas de fornecedor
      Mas os padrões convenientes são muito diferentes. Em Rust, casts de tipo inseguros exigem muitos procedimentos e cautela, e é preciso seguir mais regras do que em C
      Em especial, as referências de Rust se comportam praticamente todas como restrict, e é muito fácil estragar isso ao fazer um cast unsafe de um ponteiro bruto para uma referência segura. Por isso, se houver escolha, cria-se um forte incentivo para não escrever esse tipo de código
    • Como alguém que usa muito unsafe Rust, incluindo coisas como ponteiros com tags, discordo fortemente
      Pelo contrário, quero controle mais explícito e gostaria de reduzir esse ônus com um sistema de tipos mais expressivo. Idealmente, o sistema de tipos de Rust seria como uma variante de Prolog
    • Um “Bash ao estilo Rust” seria um Bash com tipos, especialmente ponto flutuante, com poucas situações excepcionais, funções com parâmetros explícitos e flags de linha de comando simples
  • Aprender quando investir em restrições de tipo e quando não investir é uma lição importante
    Não é um problema exclusivo de Rust, embora a forma de expressá-lo possa ser um pouco diferente
    Já lidei com C++ excessivamente tipado e Java excessivamente abstraído e tipado, e ambos têm o mesmo tipo de problema de refatoração
    Por outro lado, também vi muito Go com tipos insuficientes e pouca documentação, em que certos valores ficam espalhados por toda parte, virando minas terrestres em tempo de execução e tornando a refatoração realmente muito difícil
    A sensação inicial de progresso vem mais rápido, mas em geral você acaba entregando bugs aos usuários
    Não há uma resposta mágica para esse trade-off. Rust oferece uma gama bastante ampla de escolhas nesse eixo

  • A frase “Rust grita com você o dia inteiro, todos os dias, por coisas que, na vida anterior, você teria considerado totalmente normais” é algo que um bom compilador C também faz de forma parecida se você ativar todas as flags
    Gosto de linguagens e compiladores que permitem desligar seletivamente esses gritos e escrever código ruim de propósito. Um código ruim que funciona e que dá para escrever rápido muitas vezes é melhor do que um código perfeito que leva uma eternidade
    Você pode criar uma prova de conceito ruim, mas funcional, e depois corrigi-la para ficar menos ruim
    O ponto de que “atrai bem novos usuários, mas as bibliotecas ou ferramentas não melhoram dramaticamente, e só surgem forks pontuais para tratar casos de uso específicos” não tem relação com idade
    Atrair desenvolvedores centrais é difícil, e é preciso muito esforço para tornar isso atraente. Além disso, as convenções culturais são definidas pelos adotantes iniciais, e a ausência de convenções muitas vezes pode ser tão prejudicial quanto convenções ruins
    Olhando para Python, a abordagem frouxa em relação aos ambientes de desenvolvimento e execução resultou em umas 50 formas concorrentes de desenvolver ou executar programas Python
    O repositório de pacotes mais usado, o PyPI, foi uma bagunça por anos; pouca gente constrói em cima de pacotes existentes, os nomes parecem saídos de um gerador de palavras aleatórias, há muito código malicioso no ecossistema e nem dá para pesquisar pacotes pela linha de comando
    Isso não é culpa da linguagem, mas da comunidade e da equipe principal, que ficaram como espectadoras. A cultura é mais importante do que a tecnologia no centro dela
    Não quero criticar apenas Python; é só que conheço melhor esses problemas. C existe há meio século, mas a comunidade dele também não conseguiu organizar adequadamente nem metade das soluções que linguagens mais modernas já providenciaram

    • Rust também dá suporte a isso. Basta marcar tudo como unsafe
    • É pouco provável que Linux e Windows tenham começado a portar componentes para Rust por “não haver bibliotecas melhores”
    • Eu diria, sem medo, que isso se parece muito com TypeScript
      É aquela situação em que o compilador grita para você corrigir, mas você só quer testar uma ideia antes de deixá-la perfeitamente polida