列表
默认情况下,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
时,有两个特点应该注意:
append
操作消耗 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
来创建一个跟踪amount
和tasks
的合约:
#[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()
}
}
}