possível obter o modelo de propriedade da Rust com um wrapper C ++ genérico?


15

Examinando este artigo sobre segurança de concorrência da Rust:

http://blog.rust-lang.org/2015/04/10/Fearless-Concurrency.html

Fiquei me perguntando quantas dessas idéias podem ser alcançadas em C ++ 11 (ou mais recente). Em particular, posso criar uma classe de proprietário que transfira a propriedade para qualquer método para o qual ela possa ser passada? Parece que o C ++ tem tantas maneiras de passar variáveis ​​que seria impossível, mas talvez eu possa colocar algumas restrições na classe ou no modelo para garantir que algum código do modelo seja executado a cada passagem de método?


Algumas citações do link melhorariam esta questão #
Martin Ba

2
O @delnan (Safe) Rust garante que você nunca tenha mais de uma referência mutável a algo de cada vez e que nunca tenha uma referência mutável a algo para o qual você também está tendo referências somente leitura. Ele também possui algumas restrições na transferência de dados entre threads. Juntos, eles evitam uma classe significativa de erros relacionados ao encadeamento e facilitam o raciocínio sobre o estado dos objetos, mesmo em um único código encadeado.
CodesInChaos

3
Você não acha que pode expressar o empréstimo de uma maneira que o compilador C ++ possa verificar; portanto, você teria que recorrer à imposição de tempo de execução com a ocorrência de desempenho associada.
CodesInChaos

1
As propriedades do escopo já não foram implementadas por ponteiros inteligentes em C ++ 11?
Akshat Mahajan

1
@JerryJeremiah Rust tem uma grande variedade de tipos de referência. Os básicos, &não exigem nenhum tipo de promoção para serem utilizados. Se você tentar um &muttempo, ainda terá outra referência (mutável ou não) ao mesmo item, não poderá compilar. RefCell<T>move a verificação para o tempo de execução, para que você entre em pânico se tentar .borrow_mut()algo que já tenha um ativo .borrow()ou .borrow_mut(). O Rust também possui Rc<T>(ponteiro proprietário compartilhado) e seu irmão Weak<T>, mas esses são sobre propriedade, não mutabilidade. Cole um RefCell<T>dentro deles para mutabilidade.
8bittree

Respostas:


8

O C ++ possui três maneiras de passar parâmetros para uma função: por valor, por referência lvalue e por referência rvalue. Destes, a passagem por valor cria propriedade no sentido em que a função chamada recebe sua própria cópia, e a passagem por referência de valor indica que o valor pode ser consumido, ou seja, não será mais usado pelo chamador. Passar por referência lvalue significa que o objeto é emprestado temporariamente do chamador.

No entanto, esses tendem a ser "por convenção" e nem sempre podem ser verificados pelo compilador. E você pode acidentalmente transformar uma referência lvalue em uma referência rvalue usando std::move(). Concretamente, existem três problemas:

  • Uma referência pode sobreviver ao objeto que faz referência. O sistema de vida útil da Rust impede isso.

  • Pode haver mais de uma referência mutável / não-const ativa a qualquer momento. O verificador de empréstimos da Rust impede isso.

  • Você não pode optar por não receber referências. Você não pode ver em um site de chamada se essa função cria uma referência ao seu objeto, sem conhecer a assinatura da função chamada. Portanto, você não pode impedir referências de maneira confiável, nem excluindo métodos especiais de suas classes nem auditando o site de chamada para verificar se está em conformidade com algum guia de estilo "sem referência".

O problema da vida é sobre segurança básica da memória. Obviamente, é ilegal usar uma referência quando o objeto referenciado expirou. Mas é muito fácil esquecer a vida útil quando você armazena uma referência em um objeto, principalmente quando esse objeto sobrevive ao escopo atual. O sistema do tipo C ++ não pode dar conta disso, porque não modela a vida útil do objeto.

O std::weak_ptrponteiro inteligente codifica a semântica de propriedade semelhante a uma referência simples, mas requer que o objeto referenciado seja gerenciado por meio de a shared_ptr, isto é, é contado por referência. Esta não é uma abstração de custo zero.

Embora o C ++ tenha um sistema const, isso não rastreia se um objeto pode ser modificado, mas rastreia se um objeto pode ser modificado por meio dessa referência específica . Isso não fornece garantias suficientes para "concorrência sem medo". Por outro lado, Rust garante que, se houver uma referência mutável ativa que seja a única referência (“Eu sou o único que pode alterar esse objeto”) e se houver referências não mutáveis, todas as referências ao objeto serão não mutáveis ("Enquanto eu posso ler o objeto, ninguém pode alterá-lo").

No C ++, você pode ficar tentado a proteger o acesso a um objeto por meio de um ponteiro inteligente com um mutex. Mas, como discutido acima, uma vez que temos uma referência, ele pode escapar da vida útil esperada. Portanto, esse ponteiro inteligente não pode garantir que é o único ponto de acesso ao seu objeto gerenciado. Esse esquema pode realmente funcionar na prática porque a maioria dos programadores não deseja sabotar a si mesmos, mas, do ponto de vista do sistema de tipos, isso ainda é completamente doentio.

O problema geral dos ponteiros inteligentes é que eles são bibliotecas sobre a linguagem principal. O conjunto de recursos da linguagem principal permite esses ponteiros inteligentes, por exemplo, std::unique_ptrprecisa de construtores de movimento. Mas eles não podem corrigir deficiências no idioma principal. A capacidade de criar referências implicitamente ao chamar uma função e ter referências pendentes juntas significa que a linguagem C ++ principal é incorreta. A incapacidade de limitar referências mutáveis ​​a uma única significa que o C ++ não pode garantir segurança contra condições de corrida com qualquer tipo de simultaneidade.

É claro que em muitos aspectos, C ++ e Rust são mais parecidos do que parecidos, em particular no que diz respeito a seus conceitos de vida útil de objetos determinados estaticamente. Porém, embora seja possível escrever programas C ++ corretos (desde que nenhum dos programadores cometa erros), o Rust garante a correção em relação às propriedades discutidas.


Se o problema é que o C ++ não rastreia a propriedade na linguagem principal, seria possível implementar essa funcionalidade por meio de metaprogramação? O que significa que você iria criar uma nova classe ponteiro inteligente, que seria seguro de memória (1) forçando-o a apontar exclusivamente a objetos que utilizam apenas ponteiros inteligentes da mesma classe e (2) rastreamento de propriedade através de modelos
Elliot Gorokhovsky

2
@ ElliotGorokhovsky Não, porque o modelo não pode desativar os principais recursos do idioma, como referências. Um ponteiro inteligente pode dificultar a obtenção de uma referência, mas nesse momento você está enfrentando o idioma - a maioria das funções padrão da biblioteca precisa de referências. Também não é possível verificar a vida útil de uma referência por meio de modelos, porque o idioma não oferece um conceito reificado de vida útil.
amon

Entendo, obrigada #
Elliot Gorokhovsky
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.