学习 Cairo - 数据类型(1)
2023-05-2710:10
Reddio
2023-05-27 10:10
Reddio
2023-05-27 10:10
收藏文章
订阅专栏

预先准备 (Prerequisite)

Install cairo-run.

数据类型 (Data Type)

Cairo 与 Solidity 都是静态类型语言,意味着 Cairo 的数据类型在编译时就需要明确,并且后面不能更改。比如定义一个 u128 类型变量 a,那么对于 a 的后续赋值只能是 u128 类型的:

use debug::PrintTrait;
fn main() {
let mut a: u128 = 1_u128;
a = true;
}

true 赋值给 u128 类型变量,编译错误:

error: Unexpected argument type. Expected: "core::integer::u128", found: "core::bool".
--> main.cairo:4:9
a = true;
^**^

Felt 类型

在 Cairo 中,如果对变量不指定类型,那么默认类型是 field element,使用 felt252 关键字表示。这对于 Solidity 开发者来说是一个新概念,在 Solidity 中,所有变量都需要显式指定类型,而由于 Cairo 中存在类型推断机制,可以不指定类型,那么 Cairo 就会自动将变量设置为 felt252 类型。

fn main() {
let a = 1;
let b: u128 = 1;
}

在上面的例子中,afelt252 类型,而 bu128 类型。

整数类型

felt252 类型是 Cairo 中所有类型的基础,整数类型也是基于 felt252 实现的。但是在开发中推荐使用整数类型而不是 felt252 类型,因为整数类型拥有更多的安全检查,并且类型更加明确,也有利于开发与维护。

Cairo 中的整数类型包括下面这几种:

  • u8

  • u16

  • u32

  • u64

  • u128

  • u256

  • usize

整数代表该类型的大小,例如 u8 表示该类型是 8 bit。usize 目前暂时表示 u32,后期可能会有改动。u256 与其他类型不同,它并不是原生的 256 bit,而是由两个 u128 组合成的类型,这是由 Cairo 底层架构决定的。

与 Cairo 相比,Solidity 中的整数类型就要多出许多,从 uint8 一直到 uint256,每隔 8 就是一个整数类型,例如 uint248uint40 都是 Cairo 中不存在的。同时,Cairo 目前只支持无符号整数,即不支持负数。

Cairo 支持整数的基本四则运算,包括加减乘除以及取模 (remainder),运算规则与 Solidity 相同:

fn main() {
// addition
let sum = 5_u128 + 10_u128;

// subtraction
let difference = 95_u128 - 4_u128;

// multiplication
let product = 4_u128 * 30_u128;

// division
let quotient = 56_u128 / 32_u128; //result is 1
let quotient = 64_u128 / 32_u128; //result is 2

// remainder
let remainder = 43_u128 % 5_u128; // result is 3
}

Bool 类型

Cairo 中也支持 Bool 类型,值分别为 truefalse

fn main() {
let t = true;

let f: bool = false; // with explicit type annotation
}

短字符串类型

Cairo 没有原生的 string 类型,但是可以利用 felt252 存储一个 short string 类型。它最多可以存放 31 个字符,以便能够放在 252 bit 中。可以这样定义一个 short string:

let my_first_char = 'C';
let my_first_string = 'Hello world';

注意定义 short string 时需要使用单引号

在 Cairo 中,对于参数进行校验需要使用 assert 关键词,例如:

fn test(a: u128) {
assert(a < 100, 'a is too big');
}

其中错误信息这里也是一个 short string,它的长度不能超过 31。如果超过了 31 就会报错:

fn test(a: u128) {
assert(a < 100, 'a is too big xxxxxxxxxxxxxxxxxxx');
}

错误信息:

error: The value does not fit within the range of type core::felt252.
--> main.cairo:9:21
assert(a < 100, 'a is too big xxxxxxxxxxxxxxxxxxx');
^********************************^

Error: failed to compile: main.cairo

类型转换 (Type casting)

Cairo 支持 felt252 类型和整数类型之间互相转换。通过导入 TryIntoInto traits,就可以使用 try_intointo 方法进行转换。

来看一个例子:

use traits::TryInto;
use option::OptionTrait;

fn main(){
let my_felt = 255;
let my_u8: u8 = my_felt.try_into().unwrap();
}

这里我们定义了一个 felt252 变量 my_felt,赋值为 255,下一行利用 try_into().unwrap() 将它转换为 u8 类型。try_into() 会返回 Option<T> 类型,因此后面需要 unrwap() 来将其 unwrap。

编译运行,没有错误。我们再尝试将其赋值为 256:

use traits::TryInto;
use option::OptionTrait;

fn main(){
let my_felt = 256;
let my_u8: u8 = my_felt.try_into().unwrap();
}

这样就会出错:

Run panicked with err values: [29721761890975875353235833581453094220424382983267374]

因为 u8 类型只能存储 8 bit 的数据,也就是最大 255,如果赋值为 256 就会出错。此时就体现出的 try_into 的作用,因为我们是需要将大范围类型转换为小范围类型,可能会出现溢出的问题,因此需要尝试转换。如果我们是将小范围类型转换为大范围类型的话,直接使用 into 转换即可:

use traits::Into;
use option::OptionTrait;

fn main(){
let my_u8: u8 = 1_u8;
let my_felt: felt252 = my_u8.into();
}

当使用 try_intointo 转换时,新变量的类型必须显式指定:

use traits::TryInto;
use traits::Into;
use option::OptionTrait;

fn main(){
let my_felt = 10;
let my_u8: u8 = my_felt.try_into().unwrap(); // Since a felt252 might not fit in a u8, we need to unwrap the Option<T> type
let my_u16: u16 = my_felt.try_into().unwrap();
let my_u32: u32 = my_felt.try_into().unwrap();
let my_u64: u64 = my_felt.try_into().unwrap();
let my_u128: u128 = my_felt.try_into().unwrap();
let my_u256: u256 = my_felt.into(); // As a felt252 is smaller than a u256, we can use the into() method
let my_usize: usize = my_felt.try_into().unwrap();
let my_felt2: felt252 = my_u8.into();
let my_felt3: felt252 = my_u16.into();
}

元组类型 (The Tuple Type)

与 Python 类型,Cairo 中也存在元组类型,它是一种将若干数据组合在一起的类型。元组在定义之后长度就是固定的,不可以变化。

fn main() {
let tup: (u32, u64, bool) = (10, 20, true);
}

上面例子中我们定义了一个拥有三个元素的元组,可以看到元组中的元素类型不必一致。通过下面的方法可以获取到元组的各个元素:

use debug::PrintTrait;
fn main() {
let tup = (500, 6, true);

let (x, y, z) = tup;

if y == 6 {
'y is six!'.print();
}
}

这种操作称为解构 (destructuring),也可以利用元组直接声明多个变量:

fn main() {
let (x, y): (felt252, felt252) = (2, 3);
}

总结

Cairo 中有许多数据类型,其中 felt252 是最基础的类型,整数类型也是基于 felt252。我们也可以定义元组类型,其中可以包含多个数据。

Call out StarkNet Beta Testers!

Reddio is building developer tools for StarkNet to help you accelerate the process to develop StarkNet applications. We are inviting all of StarkNet developers to join our beta testing group, try out brand-new features and tell us what you think.

https://share.hsforms.com/1E88oQkqMSJifUV1CqR_WrQd30xn


【免责声明】市场有风险,投资需谨慎。本文不构成投资建议,用户应考虑本文中的任何意见、观点或结论是否符合其特定状况。据此投资,责任自负。

专栏文章
查看更多
数据请求中

推荐专栏

数据请求中

一起「遇见」未来

DOWNLOAD FORESIGHT NEWS APP

Download QR Code