常用集合类型

Cairo提供了一组常见的集合类型,可以用于存储和操作数据。这些集合旨在高效、灵活和易于使用。本节介绍了Cairo1中可用的主要集合类型:Array和Felt252Dict(即将推出)。

Array

数组是相同类型元素的集合。您可以导入array::ArrayTrait trait创建和使用数组方法。

需要注意的重要事项是,数组具有有限的修改选项。实际上,数组是队列,其值无法修改。这与一旦写入内存插槽就不能被覆盖而只能从中读取有关。您只能将项目附加到数组的末尾并使用pop_front从前面删除项目。

创建Array

使用ArrayTrait :: new()调用来创建Array。下面是创建一个数组并向其中添加3个元素的示例:

use array::ArrayTrait;

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

您可以在实例化数组时传递数组中项的预期类型:

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

更新Array

添加元素

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

    a.append(0);

删除元素

您只能使用pop_front()方法从数组的前面删除元素。此方法返回包含已移除元素的Option,或如果数组为空,则返回Option :: None。

use option::OptionTrait;
use array::ArrayTrait;
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中,内存是不可变的,这意味着一旦将元素添加到数组中,就无法修改它们。您只能将元素添加到数组的末尾,并从数组的前面删除元素。这些操作不需要内存突变,因为它们涉及更新指针而不是直接修改内存单元。

读取Array中的元素

要访问数组元素,可以使用get()或at()数组方法返回不同类型的快照。使用arr.at(index)相当于使用下标运算符arr[index]。

get函数返回Option<Box <@ T>>,这意味着如果该元素存在于数组中,则返回指向指定索引处元素的Box类型(Cairo的智能指针类型)的选项快照。如果不存在该元素,则get返回None。这种方法在您希望访问可能不在数组范围内的索引并且希望优雅地处理此类情况而不会发生崩溃时很有用。引用和快照的详细信息将在参考和快照章节中进行解释。

另一方面,at函数使用unbox()运算符直接返回指定索引处元素的快照,以提取存储在框中的值。如果索引超出范围,则会发生恐慌错误。只有当您希望程序在所提供的索引超出数组范围时发生panic错误时,才应使用at,这可以防止意外行为。

总之,当您想要在越界尝试访问时发生panic时,请使用at,并且在您希望优雅地处理这些情况而不会发生panic时,请使用get。

use array::ArrayTrait;
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的值。名为second的变量将从数组中的索引1获取值1。

下面是使用get()方法的示例:

use array::ArrayTrait;
use box::BoxTrait;
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 3.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。

使用Enum存储多种类型

如果要在数组中存储不同类型的元素,则可以使用枚举定义自定义数据类型,该类型可以容纳多种类型。

use array::ArrayTrait;
use traits::Into;

#[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是一个表示Array快照的结构体。它旨在提供对数组元素的安全和可控访问,而不修改原始数组。跨度特别适用于确保数据完整性并避免传递函数之间或执行只读操作时出现借用问题(参见“引用和快照”)

所有由Array提供的方法也可以与Span一起使用,除了append()方法。

将Array转换为span

要创建Array的Span,请调用span()方法:

array.span()

总结

您已经完成了本章!这是一个相当大的章节:您学习了变量、数据类型、函数、注释、if表达式、循环和常见集合!为了练习本章中讨论的概念,请尝试编写以下程序:

  • 生成第n个斐波那契数。
  • 计算数字n的阶乘。

当您准备好继续时,我们将讨论Cairo与Rust共享的一个概念,在其他编程语言中通常不存在:所有权。