Saltar al contenido
0%

¿Qué son los arrays en Solidity?

Alchemy team headshot

Escrito por Alchemy

Publicado el 22 de septiembre de 20237 min de lectura

Los arrays son estructuras de datos lineales que almacenan una cantidad fija de elementos del mismo tipo de datos, los cuales se almacenan en ubicaciones de memoria contiguas y adyacentes. Cada elemento del array se especifica por su ubicación, comúnmente conocida como su índice.

Los arrays funcionan con un sistema basado en índices, que va de 0 a (n-1), donde n representa el tamaño total del array. En lugar de crear varias variables individuales del mismo tipo, los desarrolladores solo necesitan declarar un array del tamaño que prefieran para almacenar elementos, a los cuales se puede acceder mediante el índice.

En Solidity, el principal lenguaje de programación web3 usado en Ethereum y en blockchains compatibles con EVM, un array puede ser de tamaño fijo o dinámico. Al ser un lenguaje de programación orientado a objetos, Solidity admite herencia entre smart contracts, donde varios contratos pueden heredarse en un solo contrato.

Si quieres aprender más a fondo sobre los arrays de Solidity, inscríbete en el curso gratuito de desarrollador de Solidity de Alchemy University.

¿Qué son los arrays dinámicos y fijos?

El tamaño de los arrays dinámicos no se predefine al momento de declararlos, a diferencia de los arrays fijos, que tienen un tamaño predefinido. A medida que se agregan elementos de forma sistemática, el tamaño del array dinámico cambia, y durante la ejecución se determina el tamaño real del array.

En cambio, los arrays fijos tienen un tamaño predefinido, y la cantidad de elementos presentes en el array no debe superar ese tamaño. En casos poco frecuentes en los que no se especifica el tamaño del array, se crea un array de "tamaño suficiente". Un array de "tamaño suficiente" que alcance para contener la inicialización.

Arrays de storage vs. arrays de memory

En Solidity hay dos tipos de arrays que los desarrolladores deben considerar: los arrays de storage y los arrays de memory.

Arrays de storage

Los arrays de storage normalmente se declaran como variables de estado y pueden tener tamaño fijo o dinámico. Es importante notar que los arrays de storage con longitud dinámica pueden redimensionarse, lo que significa que tienen acceso a las funciones push() y pop(), que permiten agregar y quitar elementos del array, respectivamente.

Arrays de memory

Los arrays de memory se declaran con memory como su ubicación de datos. Al igual que los arrays de storage, los arrays de memory también pueden tener longitud fija o dinámica en tiempo de compilación, sin embargo, no pueden redimensionarse una vez asignada la memoria. Esto significa que no se pueden usar las funciones de Solidity pop() ni pull().

Los arrays de memory de tamaño fijo se asignan automáticamente al declararlos, como en la instrucción uint256[5] memory numbers. Sin embargo, para asignar arrays de memory dinámicos, es necesario usar el operador new. Por ejemplo, podrías usar uint256[] memory numbers = new uint256[](5)``;

Nota: siempre inicializa el array antes de usarlo para obtener una dirección válida para su uso.

Arrays de Solidity vs. mapping

Mapping en Solidity es similar a un array en que es un tipo de referencia pensado para almacenar un conjunto de datos. La sintaxis y estructura de los mappings es bastante distinta, lo que le permite cumplir un propósito único e importante. Un mapping es una tabla de claves y valores, cada uno con su propio tipo predefinido. Un mapping puede entenderse como iniciar una tabla vacía que existe dentro de un smart contract de Solidity, esperando ser llenada con datos.

A diferencia de un array, un mapping no tiene una longitud que se pueda obtener, ni la clave o el valor deben "establecerse" desde la etapa de inicialización. Además, no es posible recorrer un mapping en un ciclo como sí es posible hacerlo con los arrays de Solidity.

Sin embargo, obtener un dato de un mapping es mucho más eficiente que obtener ese mismo dato de un array. Para obtener datos de un array es necesario recorrer todo el array hasta encontrar el elemento particular que se busca, mientras que un mapping obtiene ese dato de inmediato.

Los beneficios de rendimiento al obtener datos de mappings pueden ser bastante importantes para ahorrar gas en smart contracts de Ethereum, dado que las transacciones que modifican datos en la blockchain requieren el pago de comisiones de gas. Por lo tanto, almacenar u obtener datos de un smart contract de la manera más eficiente posible puede ahorrarles ETH a los desarrolladores con el tiempo.

En resumen, si necesitas recorrer un conjunto de datos, usando por ejemplo un for loop, entonces usa un array. Si no es necesario recorrer un conjunto de datos y, en cambio, puedes obtener valores a partir de una clave conocida, entonces considera usar un mapping.

Declarar e inicializar un array

Declarar un array en Solidity es relativamente simple. Se debe especificar el tipo de datos de los elementos y la cantidad de elementos, el tamaño del array debe ser un entero positivo, y el tipo de datos debe ser válido en Solidity.

Por ejemplo, la instrucción básica a continuación inicializaría el array y, una vez insertados los datos, estos aparecerían en el console.log:

Cada elemento del array se llama elemento, y a cada elemento se puede acceder mediante su índice numérico a través de la función "n - 1", donde n representa el número del elemento.

Por lo general, la numeración asociada a los elementos comienza en 0. Esto significa que al décimo elemento se accede en el índice 9, al undécimo elemento en el índice 10, etc.

La restricción relacionada con el mismo tipo de datos es importante de recordar porque los arrays se almacenan en celdas de memoria consecutivas, lo que implica que cada celda debe ser del mismo tipo y, por lo tanto, del mismo tamaño.

Structs

Los structs permiten a los programadores definir sus propios tipos de datos. Una vez definido un struct, puede usarse como variable de estado o en múltiples otras funciones, ya sea con argumentos posicionales o con palabras clave. El segundo enfoque evita tener que recordar el orden de los miembros incluidos dentro del struct.

Aquí hay un ejemplo de un nuevo struct:

Mappings

Los mappings pueden entenderse como almacenes de clave y valor donde existen todas las claves posibles y cualquier valor puede establecerse u obtenerse en un solo paso con la clave. El KeyType puede ser cualquier tipo de valor integrado (es decir, bytes, string o cualquier tipo de contract/enum). El ValueType puede ser cualquier tipo, incluyendo mappings, arrays y structs.

Los mappings pueden declararse de la siguiente manera:

Nota: la única ubicación de datos permitida para las variables de tipo mapping es storage.

Members

Encontrar un miembro específico de un array significa recorrer el array hasta encontrar ese miembro. Los desarrolladores deben conocer dos funciones importantes: length y push.

  • Length: devuelve el tamaño del array y también se puede usar para cambiar el tamaño de un array dinámico.
  • Push: permite a los desarrolladores agregar un elemento directamente al final de un array dinámico de storage y, como resultado, devuelve la nueva longitud del array.

Resolución de problemas con arrays de Solidity

Ocasionalmente, los desarrolladores pueden encontrarse con errores de compilación, que pueden surgir de no entender bien las reglas para declarar, crear e inicializar arrays. Aquí hay algunas formas generales de resolver errores comunes con arrays de Solidity:

1. Array.length

La función array.length permite a los desarrolladores verificar la cantidad de elementos presentes en el array. El tamaño del array de memory es fijo cuando se declara, mientras que en el caso del array dinámico se define en tiempo de ejecución, por lo que se requiere length para manipularlo.

2. Abi.encode y abi.encodePacked

Cuando se llama a una función en un contrato externo, la EVM calcula un buffer de bytes que contiene la firma de la función y los argumentos, y hay dos formas de serializar los argumentos: abi.encode y abi.encodePacked.

Abi.encode codifica sus parámetros usando las especificaciones ABI. En efecto, el ABI fue diseñado para hacer llamadas a contratos, donde los parámetros se rellenan (padding) hasta 32 bytes. Si se están haciendo llamadas a un contrato, es muy probable que se esté usando esta función.

En comparación, abi.encodePacked codifica sus parámetros usando el espacio mínimo requerido por el tipo. Codificar un uint8 usará 1 byte, y se usa cuando se quiere ahorrar espacio y no se pretende llamar a un contrato.

3. Byte[] o bytes

Ambos tipos de array pueden contener una longitud arbitraria de datos de bytes en bruto. La diferencia entre byte\[\] y bytes es que bytes\[\] sigue las reglas del tipo array, y los elementos en los arrays de memory en Solidity siempre ocupan múltiplos de 32 bytes. Esto significa que si un elemento tiene menos que un múltiplo de 32 bytes, se rellenará hasta alcanzar el tamaño necesario. Por ejemplo, se podrían desperdiciar 31 bytes por cada elemento en el caso del array de tipo byte, algo que no ocurre al usar el array de tipo bytes o string.

4. String

Este array dinámico en particular es un tipo de dato UTF-8 y, a diferencia de su funcionalidad en otros lenguajes de programación, Solidity no ofrece funciones para obtener la longitud de un string ni para realizar la concatenación o comparación de dos strings. Un string puede convertirse rápidamente en un array de bytes usando bytes[].

5. Array null en Solidity

Aunque el concepto inherente de undefined, null, nil, none, etc. existe en otros lenguajes de programación como Python y JavaScript, no existe en Solidity. En cambio, los desarrolladores pueden llamarlo el concepto de cero o valor por defecto, porque cada valor obtiene un espacio en memoria una vez creado y, por lo tanto, debe contener algo.

Cómo aprender más sobre los arrays de Solidity

Para aprender más sobre el lenguaje de programación Solidity y familiarizarte con los arrays, entre otros temas fundamentales, dirígete al bootcamp de Ethereum de Alchemy University. El bootcamp de Ethereum es un curso de programación gratuito de 7 semanas, con videos guiados y proyectos prácticos.

Si los desarrolladores son nuevos en el desarrollo en general, el curso de JavaScript para Ethereum de 3 semanas de Alchemy es un excelente requisito previo antes de comenzar un bootcamp de Ethereum.

Background gradient

Construye magia blockchain

Alchemy combina los productos y herramientas de desarrollo Web3 más potentes con recursos, comunidad y un soporte legendario.