列表

默认情况下,Cairo 不支持列表类型,但您可以使用 Alexandria。您可以参考 Alexandria 文档 获取更多详细信息。

List是什么?

可以在 Starknet 存储中使用的有序值序列:

#[storage]
stuct Storage {
  amounts: List<u128>
}

接口

trait ListTrait<T> {
  fn len(self: @List<T>) -> u32;
  fn is_empty(self: @List<T>) -> bool;
  fn append(ref self: List<T>, value: T) -> u32;
  fn get(self: @List<T>, index: u32) -> Option<T>;
  fn set(ref self: List<T>, index: u32, value: T);
  fn pop_front(ref self: List<T>) -> Option<T>;
  fn array(self: @List<T>) -> Array<T>;
}

List 还实现了 IndexView,因此您可以使用熟悉的方括号表示法来访问其成员:

let second = self.amounts.read()[1];

请注意,与 get 不同的是,使用这种方括号表示法在访问越界索引时会引发 panic(崩溃)。

支持自定义类型

List 默认支持大多数 corelib 类型。如果您想在 List 中存储自己的自定义类型,该类型必须实现 Store 特性。您可以使用 #[derive(starknet::Store)] 属性让编译器自动生成。

注意事项

在使用 List 时,有两个特点应该注意:

  1. append 操作消耗 2 次存储写入操作 - 一次是为了值本身,另一次是为了更新列表的长度。
  2. 由于编译器的限制,不能使用单个内联语句进行变更操作。例如,self.amounts.read().append(42); 是不行的。你必须分两步进行:
let mut amounts = self.amounts.read();
amounts.append(42);

依赖关系

Scarb.toml 里更新您项目的依赖:

[dependencies]
(...)
alexandria_storage = { git = "https://github.com/keep-starknet-strange/alexandria.git" }

例如,我们用 List 来创建一个跟踪amounttasks的合约:

#[starknet::interface]
trait IListExample<TContractState> {
    fn add_in_amount(ref self: TContractState, number: u128);
    fn add_in_task(ref self: TContractState, description: felt252, status: felt252);
    fn is_empty_list(self: @TContractState) -> bool;
    fn list_length(self: @TContractState) -> u32;
    fn get_from_index(self: @TContractState, index: u32) -> u128;
    fn set_from_index(ref self: TContractState, index: u32, number: u128);
    fn pop_front_list(ref self: TContractState);
    fn array_conversion(self: @TContractState) -> Array<u128>;
}

#[starknet::contract]
mod ListExample {
    use alexandria_storage::list::{List, ListTrait};

    #[storage]
    struct Storage {
        amount: List<u128>,
        tasks: List<Task>
    }

    #[derive(Copy, Drop, Serde, starknet::Store)]
    struct Task {
        description: felt252,
        status: felt252
    }


    #[abi(embed_v0)]
    impl ListExample of super::IListExample<ContractState> {
        fn add_in_amount(ref self: ContractState, number: u128) {
            let mut current_amount_list = self.amount.read();
            current_amount_list.append(number);
        }

        fn add_in_task(ref self: ContractState, description: felt252, status: felt252) {
            let new_task = Task { description: description, status: status };
            let mut current_tasks_list = self.tasks.read();
            current_tasks_list.append(new_task);
        }

        fn is_empty_list(self: @ContractState) -> bool {
            let mut current_amount_list = self.amount.read();
            current_amount_list.is_empty()
        }

        fn list_length(self: @ContractState) -> u32 {
            let mut current_amount_list = self.amount.read();
            current_amount_list.len()
        }

        fn get_from_index(self: @ContractState, index: u32) -> u128 {
            self.amount.read()[index]
        }

        fn set_from_index(ref self: ContractState, index: u32, number: u128) {
            let mut current_amount_list = self.amount.read();
            current_amount_list.set(index, number);
        }

        fn pop_front_list(ref self: ContractState) {
            let mut current_amount_list = self.amount.read();
            current_amount_list.pop_front();
        }

        fn array_conversion(self: @ContractState) -> Array<u128> {
            let mut current_amount_list = self.amount.read();
            current_amount_list.array()
        }
    }
}
Last change: 2023-11-27, commit: e112ed3