Asignando variable de memoria a variable de almacenamiento, ¿qué sucede?

Tengo curiosidad por saber qué sucede si intentas hacer lo siguiente:

contract C {
    Struct S {
        uint a;
        uint b;
    }

    mapping(address => s) structs;

    function updateStructs(S sInstance) private {
        structs[msg.sender] = sInstance;
    }

    function addStruct (uint _a, uint _b) payable {
        S memory s = S({a: _a, b: _b});
        updateStructs(s);
    }
}

Como entendí cómo funcionan las variables, addStruct crea una variable de memoria que solo tiene un alcance de función y se desasignará una vez que se procese esta transacción/mensaje. Entonces, ¿qué quedará structs[msg.sender]después de llamar a addStruct? ¿Será cambiar updateStructs(S sInstance)para updateStructs(S storage sInstance)cambiar algo?

sno se desasigna hasta que addStructregresa. Puede ser útil comprender los marcos de pila o los registros de activación.

Respuestas (2)

Cuando almacena una variable de memoria en el almacenamiento, simplemente copiará el objeto en el almacenamiento.

El uso de la palabra clave de almacenamiento en updateStructs en realidad no hará nada. Las estructuras y matrices en las funciones son variables de almacenamiento predeterminadas, por lo que el objeto de memoria se copiaría en el almacenamiento justo cuando llama a updateStructs sin la palabra clave de almacenamiento.

Editar: estoy corregido. @LibertyLocked tiene razón en su respuesta donde dice que no puede convertir implícitamente de memoria a almacenamiento, por lo que agregar la palabra clave de almacenamiento al argumento hará que la compilación falle.

Gracias por notarlo, lo edité. Seguro de esto, ¿lo intentaste? Eso es más obvio para mí, pero quería confirmación.
Mi primera parte, sí. Copiará el objeto en la memoria en el almacenamiento. Puede probar esto en Remix agregando una función getStruct que obtiene los datos de structs[msg.sender] de otra transacción. Si no se copiaron en el almacenamiento, estos datos no existirían. No me equivoqué en mi segunda parte, ya que @LibertyLocked tenía razón al decir que habría un error de compilación. También actualicé mi respuesta.

En su ejemplo, si cambiara su código para que se vea así

function updateStructs(S storage sInstance) private {
    structs[msg.sender] = sInstance;
}

function addStruct (uint _a, uint _b) payable {
    S memory s = S({a: _a, b: _b});
    updateStructs(s);
}

El código no se compilará por las siguientes razones:

  • updateStructsespera una instancia de estructura del almacenamiento, pero le está pasando una instancia de estructura en la memoria.
  • No hay forma de que Solidity descubra dónde asignar y colocar el salmacenamiento addStruct.
  • Cuando tiene S memory sInstanceen su updateStruct, su sse copia en el marco de pila de updateStructs. Además, la sentrada addStructno se desasigna hasta que regresa de addStruct(de lo contrario, ¿cómo continuaría ejecutándose la función una vez que regresa de updateStruct?).

Para responder a su pregunta, en su ejemplo debe usar memoryen lugar de storage. De hecho, está implícitamente memoryen los argumentos de función.

OK, para resumir: en S memory s = S({a: _a, b: _b});la variable de memoria se creará en el alcance de addStruct, luego, en updateStructs(s);el valor de sse copia en el marco de la pila de updateStructsy luego sse copia del marco de la pila en el almacenamiento en structs[msg.sender] = sInstance;, ¿verdad? Mi pregunta era qué habrá en este lugar en el almacenamiento.
Solo hay una ubicación de almacenamiento y esa es structs[msg.sender]. Solo se modifica en updateStructs. storagepalabra clave significa una referencia a la ubicación de almacenamiento
Entonces, si asigna una variable de tipo de referencia que está en la pila de funciones a una variable que está almacenada, ¿esa variable se copia en el almacenamiento?
La referencia se puede copiar, pero el almacenamiento en sí nunca se modifica hasta que se escribe en la referencia.
Creo que nos resulta difícil entendernos :) Así es como lo entiendo: si pasas , asigna bytes sen updateStructsla pila y smantiene la referencia a esos bytes. Una vez que haces structs[msg.sender] = sInstancelo que sucede? Si se copia la referencia s, no tiene ningún sentido, ya que los bytes asignados a la pila son válidos solo en el ámbito de la función y structs[msg.sender]apuntarán a bytes aleatorios según el nodo en el que se ejecute. Si los bytes asignados de la pila se copian structs[msg.sender], está bien, pero aún no obtuve confirmación de esto de sus respuestas.