Arrays

Un array es una colección de elementos del mismo tipo. Puedes crear y utilizar métodos de array importando el trait array::ArrayTrait.

An important thing to note is that arrays have limited modification options. Arrays are, in fact, queues whose values can't be modified. This has to do with the fact that once a memory slot is written to, it cannot be overwritten, but only read from it. You can only append items to the end of an array and remove items from the front using pop_front.

Creating an Array

Crear una matriz se realiza con la llamada ArrayTrait::new()`. Aquí tienes un ejemplo de la creación de una matriz a la que agregamos 3 elementos:

fn main() {
    let mut a = ArrayTrait::new();
    a.append(0);
    a.append(1);
    a.append(2);
}

When required, you can pass the expected type of items inside the array when instantiating the array like this, or explicitly define the type of the variable.

let mut arr = ArrayTrait::<u128>::new();
let mut arr:Array<u128> = ArrayTrait::new();

Updating an Array

Adding Elements

To add an element to the end of an array, you can use the append() method:

fn main() {
    let mut a = ArrayTrait::new();
    a.append(0);
    a.append(1);
    a.append(2);
}

Removing Elements

You can only remove elements from the front of an array by using the pop_front() method. This method returns an Option containing the removed element, or Option::None if the array is empty.

use debug::PrintTrait;

fn main() {
    let mut a = ArrayTrait::new();
    a.append(10);
    a.append(1);
    a.append(2);

    let first_value = a.pop_front().unwrap();
    first_value.print(); // print '10'
}

El código anterior imprimirá 10 cuando eliminemos el primer elemento añadido.

En Cairo, la memoria es inmutable, lo que significa que no es posible modificar los elementos de un array una vez que han sido añadidos. Sólo se pueden añadir elementos al final de un array y eliminar elementos de la parte frontal de un array. Estas operaciones no requieren mutación de memoria, ya que implican actualizar punteros en lugar de modificar directamente las celdas de memoria.

Reading Elements from an Array

Para acceder a los elementos de un array, puedes utilizar los métodos get() o at() que devuelven diferentes tipos. Utilizar arr.at(index) es equivalente a utilizar el operador de subíndice arr[index].

The get function returns an Option<Box<@T>>, which means it returns an option to a Box type (Cairo's smart-pointer type) containing a snapshot to the element at the specified index if that element exists in the array. If the element doesn't exist, get returns None. This method is useful when you expect to access indices that may not be within the array's bounds and want to handle such cases gracefully without panics. Snapshots will be explained in more detail in the References and Snapshots chapter.

The at function, on the other hand, directly returns a snapshot to the element at the specified index using the unbox() operator to extract the value stored in a box. If the index is out of bounds, a panic error occurs. You should only use at when you want the program to panic if the provided index is out of the array's bounds, which can prevent unexpected behavior.

En resumen, usa at cuando quieras que el programa entre en pánico ante intentos de acceso fuera de los límites, y usa get cuando prefieras manejar estos casos con gracia sin entrar en pánico.

fn main() {
    let mut a = ArrayTrait::new();
    a.append(0);
    a.append(1);

    let first = *a.at(0);
    let second = *a.at(1);
}

In this example, the variable named first will get the value 0 because that is the value at index 0 in the array. The variable named second will get the value 1 from index 1 in the array.

He aquí un ejemplo con el método get():

fn main() -> u128 {
    let mut arr = ArrayTrait::<u128>::new();
    arr.append(100);
    let index_to_access =
        1; // Change this value to see different results, what would happen if the index doesn't exist?
    match arr.get(index_to_access) {
        Option::Some(x) => {
            *x
                .unbox() // Don't worry about * for now, if you are curious see Chapter 4.2 #desnap operator
        // It basically means "transform what get(idx) returned into a real value"
        },
        Option::None => {
            let mut data = ArrayTrait::new();
            data.append('out of bounds');
            panic(data)
        }
    }
}

Para determinar el número de elementos de un array, utilice el método len(). El valor devuelto es de tipo usize.

Si quieres comprobar si un array está vacío o no, puedes utilizar el método is_empty(), que devuelve true si el array está vacío y false en caso contrario.

Storing multiple types with Enums

If you want to store elements of different types in an array, you can use an Enum to define a custom data type that can hold multiple types. Enums will be explained in more detail in the Enums and Pattern Matching chapter.

#[derive(Copy, Drop)]
enum Data {
    Integer: u128,
    Felt: felt252,
    Tuple: (u32, u32),
}

fn main() {
    let mut messages: Array<Data> = ArrayTrait::new();
    messages.append(Data::Integer(100));
    messages.append(Data::Felt('hello world'));
    messages.append(Data::Tuple((10, 30)));
}

Span

Span is a struct that represents a snapshot of an Array. It is designed to provide safe and controlled access to the elements of an array without modifying the original array. Span is particularly useful for ensuring data integrity and avoiding borrowing issues when passing arrays between functions or when performing read-only operations (cf. References and Snapshots)

Todos los métodos proporcionados por Array también se pueden utilizar con Span, a excepción del método append().

Turning an Array into span

To create a Span of an Array, call the span() method:

fn main() {
    let mut array: Array<u8> = ArrayTrait::new();
    array.span();
}
Last change: 2023-11-19, commit: a15432b