数组

An array is a collection of elements of the same type. You can create and use array methods by using the ArrayTrait trait from the core library.

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.

Creating an Array

Creating an array is done with the ArrayTrait::new() call. Here's an example of creating an array and appending 3 elements to it:

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

需要时,你可以在实例化数组时像下面这样传递数组内部元素的预期类型,或者明确定义变量的类型。

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

Updating an Array

Adding Elements

要在一个数组的末尾添加一个元素,可以使用append()方法:

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 that can be unwrapped, containing the removed element, or Option::None if the array is empty.

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

    let first_value = a.pop_front().unwrap();
    println!("The first value is {}", first_value);
}

The above code will print The first value is 10 as we remove the first element that was added.

在Cairo中,内存是不可改变的,这意味着一旦数组中的元素被添加,就不可能修改它们。你只能将元素添加到数组的末端,并从数组的前端移除元素。这些操作不需要内存突变,因为它们涉及到更新指针而不是直接修改内存单元。

Reading Elements from an Array

为了访问数组元素,你可以使用get()at()数组方法,它们返回不同的类型。使用arr.at(index)等同于使用下标操作符arr[index]

get() Method

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.

下面是一个使用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 => { panic!("out of bounds") }
    }
}

at() Method

The at function, and its equivalent the subscripting operator, on the other hand, directly return 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.

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

    // using `at()` method
    assert(*a.at(0) == 0, 'item mismatch on index 0');
    // using subscripting operator
    assert(*a[1] == 1, 'item mismatch on index 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.

总之,当你想对越界访问尝试进行恐慌时,请使用at,而当你想优雅地处理这种情况而不恐慌时,请使用get

To determine the number of elements in an array, use the len() method. The return value is of type usize.

如果你想检查一个数组是否为空,你可以使用is_empty()方法,如果数组为空,返回true,否则返回false

array! Macro

Sometimes, we need to create arrays with values that are already known at compile time. The basic way of doing that is redundant. You would first declare the array and then append each value one by one. array! is a simpler way of doing this task by combining the two steps. At compile-time, the compiler will expand the macro to generate the code that appends the items sequentially.

不使用 array!:

    let mut arr = ArrayTrait::new();
    arr.append(1);
    arr.append(2);
    arr.append(3);
    arr.append(4);
    arr.append(5);

使用 array!:

    let arr = array![1, 2, 3, 4, 5];

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> = array![];
    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, as introduced in "References and Snapshots".

All methods provided by Array can also be used with Span, except for the append() method.

Turning an Array into Span

要创建一个 ArraySpan ,请调用span()方法:

fn main() {
    let mut array: Array<u8> = ArrayTrait::new();
    array.span();
}