Vamos começar postulando que a memória é de longe (dezenas, centenas ou até milhares de vezes) mais comum do que todos os outros recursos combinados. Cada variável, objeto, membro do objeto precisa de alguma memória alocada e liberada posteriormente. Para cada arquivo aberto, você cria dezenas a milhões de objetos para armazenar os dados extraídos do arquivo. Todo fluxo TCP acompanha um número ilimitado de cadeias de bytes temporárias criadas para serem gravadas no fluxo. Estamos na mesma página aqui? Ótimo.
Para que o RAII funcione (mesmo que você tenha ponteiros inteligentes prontos para cada caso de uso sob o sol), é necessário ter a propriedade correta. Você precisa analisar quem deve ser o proprietário desse ou daquele objeto, quem não deve e quando a propriedade deve ser transferida de A para B. Claro, você pode usar a propriedade compartilhada para tudo , mas depois emula um GC por meio de ponteiros inteligentes. Nesse ponto, fica muito mais fácil e rápido criar o GC no idioma.
A coleta de lixo libera você dessa preocupação com a memória de longe o recurso mais comumente usado. Claro, você ainda precisa tomar a mesma decisão para outros recursos, mas esses são muito menos comuns (veja acima), e a propriedade complicada (por exemplo, compartilhada) também é menos comum. A carga mental é reduzida significativamente.
Agora, você cita algumas desvantagens de tornar todos os valores coletados como lixo. No entanto, a integração de GC e tipos de valor com segurança de memória com RAII em um idioma é extremamente difícil, portanto, talvez seja melhor migrar essas compensações por outros meios?
A perda de determinismo acaba por não ser tão ruim na prática, porque afeta apenas a vida determinística do objeto . Conforme descrito no próximo parágrafo, a maioria dos recursos (além da memória, que é abundante e pode ser reciclada preguiçosamente) não está vinculada ao tempo de vida do objeto nessas linguagens. Existem alguns outros casos de uso, mas eles são raros na minha experiência.
Atualmente, seu segundo ponto, gerenciamento manual de recursos, é abordado por meio de uma declaração que executa a limpeza com base no escopo, mas não acopla essa limpeza ao tempo de vida do objeto (portanto, não interage com o GC e a segurança da memória). Isso está using
em C #, with
em Python, try
com recursos nas versões recentes do Java.