Referencias

Move tiene dos tipos de referencias: inmutables &y mutables &mut. Las referencias inmutables son de solo lectura y no pueden modificar el valor subyacente (ni ninguno de sus campos). Las referencias mutables permiten modificaciones mediante una escritura a través de esa referencia. El sistema de tipos de Move impone una disciplina de propiedad que evita errores de referencia.

Para obtener más detalles sobre las reglas de referencia, consulte Estructuras y recursos.

Operadores de referencia

Move proporciona operadores para crear y ampliar referencias, así como para convertir una referencia mutable en una inmutable. Aquí y en otros lugares utilizamos la notación e: Tpara "la expresión etiene tipo T".

Sintaxis

Tipo

Descripción

&e

&Tdonde e: Ty Tes un tipo sin referencia

Crear una referencia inmutable ae

&mut e

&mut Tdonde e: Ty Tes un tipo sin referencia

Cree una referencia mutable a e.

&e.f

&Tdóndee.f: T

Cree una referencia inmutable al campo fde estructura e.

&mut e.f

&mut Tdóndee.f: T

Cree una referencia mutable al campo fde estructura e.

freeze(e)

&Tdóndee: &mut T

Convierta la referencia mutable een una referencia inmutable.

Los operadores &e.fy &mut e.fse pueden utilizar tanto para crear una nueva referencia en una estructura como para ampliar una referencia existente:

Copiar

let s = S { f: 10 };
let f_ref1: &u64 = &s.f; // works
let s_ref: &S = &s;
let f_ref2: &u64 = &s_ref.f // also works

Una expresión de referencia con múltiples campos funciona siempre que ambas estructuras estén en el mismo módulo:

Copiar

struct A { b: B }
struct B { c : u64 }
fun f(a: &A): &u64 {
  &a.b.c
}

Finalmente, tenga en cuenta que no se permiten referencias a referencias:

Copiar

let x = 7;
let y: &u64 = &x;
let z: &&u64 = &y; // will not compile

Lectura y escritura a través de referencias

Se pueden leer referencias mutables e inmutables para producir una copia del valor al que se hace referencia.

Sólo se pueden escribir referencias mutables. Una escritura *x = vdescarta el valor previamente almacenado xy lo actualiza con v.

Ambas operaciones utilizan la *sintaxis similar a C. Sin embargo, tenga en cuenta que una lectura es una expresión, mientras que una escritura es una mutación que debe ocurrir en el lado izquierdo de un igual.

Sintaxis

Tipo

Descripción

*e

Tdonde eesta &To&mut T

Lea el valor señalado pore

*e1 = e2

()dónde e1: &mut Tye2: T

Actualice el valor e1con e2.

Para que se pueda leer una referencia, el tipo subyacente debe tener la copycapacidad de leer la referencia para crear una nueva copia del valor. Esta regla impide la copia de valores de recursos:

Copiar

fun copy_resource_via_ref_bad(c: Coin) {
    let c_ref = &c;
    let counterfeit: Coin = *c_ref; // not allowed!
    pay(c);
    pay(counterfeit);
}

Doblemente: para poder escribir en una referencia, el tipo subyacente debe tener la dropcapacidad, ya que escribir en la referencia descartará (o "eliminará") el valor anterior. Esta regla evita la destrucción de valores de recursos:

Copiar

fun destroy_resource_via_ref_bad(ten_coins: Coin, c: Coin) {
    let ref = &mut ten_coins;
    *ref = c; // not allowed--would destroy 10 coins!
}

freezeinferencia

Se puede utilizar una referencia mutable en un contexto donde se espera una referencia inmutable:

Copiar

let x = 7;
let y: &u64 = &mut x;

Esto funciona porque, bajo el capó, el compilador inserta freezeinstrucciones donde son necesarias. Aquí hay algunos ejemplos más de freezeinferencia en acción:

Copiar

fun takes_immut_returns_immut(x: &u64): &u64 { x }

// freeze inference on return value
fun takes_mut_returns_immut(x: &mut u64): &u64 { x }

fun expression_examples() {
    let x = 0;
    let y = 0;
    takes_immut_returns_immut(&x); // no inference
    takes_immut_returns_immut(&mut x); // inferred freeze(&mut x)
    takes_mut_returns_immut(&mut x); // no inference

    assert!(&x == &mut y, 42); // inferred freeze(&mut y)
}

fun assignment_examples() {
    let x = 0;
    let y = 0;
    let imm_ref: &u64 = &x;

    imm_ref = &x; // no inference
    imm_ref = &mut y; // inferred freeze(&mut y)
}

Subtipificación

Con esta freezeinferencia, el verificador de tipo de movimiento puede verse &mut Tcomo un subtipo de &T. Como se muestra arriba, esto significa que en cualquier lugar para cualquier expresión donde &Tse use un valor, &mut Ttambién se puede usar un valor. Esta terminología se utiliza en mensajes de error para indicar de forma concisa que &mut Tse necesitaba un elemento donde &Tse proporcionó un. Por ejemplo

Copiar

address 0x42 {
module example {
    fun read_and_assign(store: &mut u64, new_value: &u64) {
        *store = *new_value
    }

    fun subtype_examples() {
        let x: &u64 = &0;
        let y: &mut u64 = &mut 1;

        x = &mut 1; // valid
        y = &2; // invalid!

        read_and_assign(y, x); // valid
        read_and_assign(x, y); // invalid!
    }
}
}

producirá los siguientes mensajes de error

Copiar

error:

    ┌── example.move:12:9 ───

 12 │         y = &2; // invalid!
    │         ^ Invalid assignment to local 'y'
    ·
 12 │         y = &2; // invalid!
    │             -- The type: '&{integer}'
    ·
  9 │         let y: &mut u64 = &mut 1;
    │                -------- Is not a subtype of: '&mut u64'


error:

    ┌── example.move:15:9 ───

 15 │         read_and_assign(x, y); // invalid!
    │         ^^^^^^^^^^^^^^^^^^^^^ Invalid call of '0x42::example::read_and_assign'. Invalid argument for parameter 'store'
    ·
  8 │         let x: &u64 = &0;
    │                ---- The type: '&u64'
    ·
  3 │     fun read_and_assign(store: &mut u64, new_value: &u64) {
    │                                -------- Is not a subtype of: '&mut u64'

Los únicos otros tipos actualmente que tienen subtipos son las tuplas.

Propiedad

Tanto las referencias mutables como las inmutables siempre se pueden copiar y ampliar incluso si existen copias o extensiones de la misma referencia :

Copiar

fun reference_copies(s: &mut S) {
  let s_copy1 = s; // ok
  let s_extension = &mut s.f; // also ok
  let s_copy2 = s; // still ok
  ...
}

Esto podría resultar sorprendente para los programadores familiarizados con el sistema de propiedad de Rust, que rechazarían el código anterior. El sistema de tipos de Move es más permisivo en el tratamiento de las copias, pero igualmente estricto a la hora de garantizar la propiedad única de las referencias mutables antes de la escritura.

Las referencias no se pueden almacenar

Las referencias y las tuplas son los únicos tipos que no se pueden almacenar como valores de campo de estructuras, lo que también significa que no pueden existir en el almacenamiento global. Todas las referencias creadas durante la ejecución del programa se destruirán cuando finalice un programa Move; son enteramente efímeros. Esta invariante también es válida para valores de tipos sin la storecapacidad, pero tenga en cuenta que las referencias y las tuplas van un paso más allá al no permitirse nunca en las estructuras en primer lugar.

Esta es otra diferencia entre Move y Rust, que permite almacenar referencias dentro de estructuras.

Actualmente, Move no puede admitir esto porque las referencias no se pueden serializar , pero cada valor de Move debe ser serializable . Este requisito proviene del almacenamiento global persistente de Move, que necesita serializar valores para conservarlos en las ejecuciones del programa. Las estructuras se pueden escribir en el almacenamiento global y, por lo tanto, deben ser serializables.

Uno podría imaginar un sistema de tipos más sofisticado y expresivo que permitiría almacenar referencias en estructuras y prohibiría que esas estructuras existan en el almacenamiento global. Quizás podríamos permitir referencias dentro de estructuras que no tienen la storecapacidad, pero eso no resolvería completamente el problema: Move tiene un sistema bastante complejo para rastrear la seguridad de las referencias estáticas, y este aspecto del sistema de tipos también tendría que extenderse a admite el almacenamiento de referencias dentro de estructuras. En resumen, el sistema de tipos de Move (particularmente los aspectos relacionados con la seguridad de las referencias) tendría que expandirse para admitir referencias almacenadas. Pero es algo a lo que estamos atentos a medida que evoluciona el lenguaje.

Last updated