数组

一个数组是相同类型元素的集合。你可以通过导入array::ArrayTraittrait来创建和使用数组方法。

需要注意的一个重要问题是,数组的修改选项有限。这里的数组实际上是以队列的形式存储的,其值不能被直接修改。 这与这样一个事实有关:一旦一个内存槽被写入,它就不能被覆盖,而只能从其中读出。你只能用pop_front将项目追加到数组的末端,并从前面删除项目。

创建一个数组

创建一个数组是通过调用ArrayTrait::new()完成的。下面是一个创建3个元素的数组的例子:

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();

更新一个数组

添加元素

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

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

移除元素

要从一个数组的前面移除一个元素,你可以使用pop_front()方法。 该方法返回一个包含被移除元素的Option。如果数组为空,则返回Option::None

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'
}

上面的代码将打印10,因为我们删除了第一个被添加的元素。

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

从数组中读取元素

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

函数 get 返回一个 Option<Box<@T>> ,这意味着它返回一个 Box 类型(Cairo的智能指针类型)的选项,其中包含指定索引处元素的快照(如果该元素存在于数组中)。如果元素不存在,get返回None。当你希望访问的索引可能不在数组的边界内,并希望优雅地处理这种情况而不引起panic时,该方法就非常有用。快照将在引用和快照一章中详细解释。

另一方面,at函数直接返回一个快照到指定索引的元素,使用unbox()操作符来提取存储在一个盒子里的值。如果索引超出了范围,就会抛出错误(panic)。你应该只在希望索引超出数组的边界时抛出panic时,使用 at,这样可以防止意外的行为。

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

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

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

在这个例子中,名为first'的变量将得到0'的值,因为那是数组中索引0'的值。 是数组中索引为0'的值。名为second'的变量将得到 从数组中的索引1处获得数值1'。

下面是一个使用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)
        }
    }
}

数组大小相关的方法

要确定一个数组中的元素数量,请使用len()方法。其返回值为usize类型。

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

用Enums存储多种类型

如果你想在一个数组中存储不同类型的元素,你可以使用Enum来定义一个可以容纳多种类型的自定义数据类型。更多关于Enum的细节见 Enums and Pattern Matching 这一章节。

#[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是一个结构,代表一个 "数组 "的快照(snapshot)。它被设计用来提供对数组元素的安全可控的访问,而不需要修改原始数组。Span对于确保数据的完整性和避免在函数间传递数组或执行只读操作时的借用问题特别有用(参见引用和快照)

除了 append()方法外,Array提供的其他所有方法都可以用于 Span

将一个数组变成span

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

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