¿Es posible lograr el modelo de propiedad de Rust con un contenedor genérico de C ++?

13

Revisando este artículo sobre la seguridad de concurrencia de Rust:

enlace

Me preguntaba cuántas de estas ideas se pueden lograr en C ++ 11 (o más reciente). En particular, ¿puedo crear una clase de propietario que transfiera la propiedad a cualquier método al que se pueda pasar? Parece que C ++ tiene tantas formas de pasar variables que sería imposible, pero ¿quizás podría poner algunas restricciones en la clase o plantilla para asegurar que se ejecute algún código de plantilla con cada paso de método?

    
pregunta Brannon 10.05.2016 - 18:30

1 respuesta

6

C ++ tiene tres formas de pasar parámetros a una función: por valor, por referencia de valor y por referencia de valor. De estos, pasar por valor crea propiedad en el sentido de que la función llamada recibe su propia copia, y pasar por referencia rvalue indica que el valor puede ser consumido, es decir, el llamante ya no lo usará. Pasar por una referencia de valor significa que el objeto se toma prestado temporalmente del llamante.

Sin embargo, estos tienden a ser "por convención" y no siempre pueden ser verificados por el compilador. Y accidentalmente puede convertir una referencia lvalue en una referencia rvalue usando std::move() . Concretamente, hay tres problemas:

  • Una referencia puede sobrevivir al objeto al que hace referencia. El sistema de por vida de Rust evita esto.

  • Puede haber más de una referencia mutable / no constante activa en cualquier momento. El verificador de préstamos de Rust evita esto.

  • No puede optar por no recibir referencias. No puede ver en un sitio de llamada si esa función crea una referencia a su objeto, sin conocer la firma de la función llamada. Por lo tanto, no puede evitar las referencias de manera confiable, ni eliminando ningún método especial de sus clases ni auditando el sitio de la llamada para verificar que cumpla con alguna guía de estilo "sin referencias".

El problema de la vida es sobre la seguridad de la memoria básica. Por supuesto, es ilegal usar una referencia cuando el objeto referenciado ha caducado. Pero es muy fácil olvidarse de la vida útil cuando almacena una referencia dentro de un objeto, en particular cuando ese objeto sobrevive al alcance actual. El sistema de tipo C ++ no puede explicar esto porque no modela el tiempo de vida de los objetos en absoluto.

El puntero inteligente std::weak_ptr codifica semántica de propiedad similar a una referencia simple, pero requiere que el objeto al que se hace referencia se administre a través de un shared_ptr , es decir, se cuenta como referencia. Esto no es una abstracción de costo cero.

Si bien C ++ tiene un sistema constante, esto no hace un seguimiento de si un objeto se puede modificar, pero si un objeto se puede modificar a través de esa referencia en particular . Eso no proporciona garantías suficientes para una "concurrencia intrépida". En contraste, Rust garantiza que si hay una referencia mutable activa que es la única referencia ("Yo soy el único que puede cambiar este objeto") y si hay referencias no mutables, todas las referencias al objeto no son mutables. ("Si bien puedo leer el objeto, nadie puede cambiarlo").

En C ++ puede estar tentado a proteger el acceso a un objeto a través de un puntero inteligente con un mutex. Pero como se mencionó anteriormente, una vez que tengamos una referencia, puede escapar de su vida útil esperada. Por lo tanto, tal puntero inteligente no puede garantizar que sea el único punto de acceso a su objeto gestionado. Tal esquema en realidad puede funcionar en la práctica porque la mayoría de los programadores no quieren sabotearse a sí mismos, pero desde un punto de vista del sistema de tipos, esto todavía es completamente erróneo.

El problema general con los punteros inteligentes es que son bibliotecas que se encuentran en la parte superior del lenguaje central. El conjunto de características básicas del lenguaje habilita estos punteros inteligentes, por ejemplo, std::unique_ptr necesita constructores de movimiento. Pero no pueden solucionar las deficiencias dentro del lenguaje central. Las habilidades para crear referencias implícitamente cuando se llama a una función y tener referencias que cuelgan juntas significa que el lenguaje C ++ principal no es correcto. La incapacidad de limitar las referencias mutables a una sola significa que C ++ no puede garantizar la seguridad contra las condiciones de carrera con cualquier tipo de concurrencia.

Por supuesto, en muchos aspectos, C ++ y Rust son más parecidos que disaleables, en particular con respecto a sus conceptos de vida útil objetivamente determinada. Pero si bien es posible escribir programas de C ++ correctos (siempre que ninguno de los programadores cometa errores), Rust garantiza la corrección con respecto a las propiedades comentadas.

    
respondido por el amon 16.11.2017 - 23:39

Lea otras preguntas en las etiquetas