# Cairo 之旅 II ： 通过 Starklings 掌握 Cairo 语法

By [Starknet 中文](https://paragraph.com/@starknet-zh) · 2022-10-25

---

> 作者：[Darington Nnam](https://twitter.com/0xdarlington) 原文：[Journey through Cairo II — Mastering Cairo’s Syntaxes with Starklings](https://medium.com/coinmonks/journey-through-cairo-ii-mastering-cairos-syntaxes-with-starklings-8d3cbad6946) 翻译：[Louis Wang](https://twitter.com/lviswang) 校对：[「StarkNet 中文社区」](https://twitter.com/StarkNet_ZH)

在[上篇文章](https://mirror.xyz/starknet-zh.eth/PSNMGAXKIrXvxhILjp35aM3tW4c4AnGclfzMlQ5hQ64)中，我们用 Protostar、ArgentX 和 Voyager 为 Cairo 建立本地开发环境，我们将从今天开始了解 Cairo 的基本原理，并尝试用 Starklings 作为教材来解释 Cairo 的基本功能。

### OnlyDust 的 Starklings

直接通过官方文档来学习 Cairo，对普通人比较困难。刚开始我努力的去理解这些基础知识，直到发现了 [**Starklings**](https://github.com/onlydustxyz/starklings)。

**Starklings** 是由 OnlyDust 团队编写的互动式教程，其采纳流行的渐进互动式 Rust 教程 Rustlings 的长处。今天，我们将通过解决 Starklings 中的问题，开始了解 [Cairo 如何工作](https://www.cairo-lang.org/docs/how_cairo_works/index.html)。

### 安装

为了高效学习本教程，需要提前设置你的计算机环境：

访问 Starklings 的官方 [Github repo](https://github.com/onlydustxyz/starklings)。

复制运行 git clone 指令到本地计算机上。

    git clone --branch stable --single-branch
    <https://github.com/onlydustxyz/starklings.git>
    

安装指令运行的必需工具：

    curl -L 
    <https://raw.githubusercontent.com/onlydustxyz/starklings/master/install.sh> | bash
    

![](https://storage.googleapis.com/papyrus_images/0401546d7447d4a087a52133855ea248cfb8ae0278f0d854ad796f01a95ac454.png)

开始测试，打开终端并执行：

    starklings --watch
    

![](https://storage.googleapis.com/papyrus_images/cedc3cd32d6756c1d444c38f5b94ec246c69039e86a53e412a6634f765d7af72.png)

### 命令和资源

现在我们已完成开发环境，在潜心编写 Cairo 前，这里一些必要指令和资源，会让开发更简单！

1.  在没进入观察模式前手动验证测试，请运行以下：
    
        starklings --verify relative_path_to_the_exercise_file 
        
    
2.  如果解决不了测试，可以检索答案：
    
        starklings --solution relative_path_to_the_exercise_file
        
    
3.  如果想自由发挥尝试更多，可以试试与 Solidity Remix （以太坊智能合约开发 IDE）相似的 [Cairo‘s playground](https://www.cairo-lang.org/playground/) 。
    

理解 Cairo 语法
-----------

**syntax01.cairo — 从 Cairo 合约开始**

![](https://storage.googleapis.com/papyrus_images/553ae5114076b2900486fa429bb5d3541d025ee83bdb81f735069814ab632d1f.png)

开始使用 Cairo 时，如何区分 Cairo 程序和 Cairo 合约。为了避免相同的困惑，以下是两者的快速定义：

**Cairo 程序**：Cairo 程序可以理解为一种无状态合约，意味着在编写程序时，不能访问存储，不能与其他 Cairo 程序包括 L1 链进行交互。

**Cairo 合约**：简单来说，这是运行在 Starknet 虚拟机 (VM) 上的程序。由于在虚拟机上运行，因此他们可以访问 Starknet 的持续状态，改变或修改 Starknet 的变量状态，与其他合约交互，并与底层 L1 无缝交互。

理解后，就可以开始写一个 Cairo 合约，你必须指定它是一个合约并在最顶行写上：

    %lang starknet
    

下一步，在 Starklings 的 Syntax01.cairo 中完成，并检查能否通过测试：

![](https://storage.googleapis.com/papyrus_images/789bdf777cc658c7c7665aa49d2eebeb812d4e1e50e9475fad8bc91b43843f25.png)

成功运行！

![](https://storage.googleapis.com/papyrus_images/d307202ff5e8675af338449fa3176db7c47646b900a4333ae29810bc67791c77.png)

接下来是第二个测试，删掉 **#I AM NOT DONE**，Starkligns 会引导你进入下一个测试！

**syntax02.cairo — Cairo 模块化**

就像大部分现存的编程语言一样，把所有程序代码保存在一个文件里是非常不明智的做法。你也可能需要使用他人的代码，而不是重新发明轮子，所以每种语言都鼓励代码模块化，Cairo 也不例外。

在这个测试中，我们要研究 **test\_ok** 函数，使该函数工作需要并导入所有重要文件。

在 Cairo 中导入一个文件：

    from starkware.starknet.common.syscalls import get_contract_address
    

当你这样做时，Cairo 会在当前目录和相对于编译器路径的标准库目录中搜索文件 _syscall.cairo_，并从中导入 _get\_contract\_address_ 函数。

我们的函数 **test\_ok** 需要三个隐参数：_syscall\_ptr、pedersen\_ptr_ 和 _range\_check\_ptr_（我们将在后文介绍隐参数）。但我们注意到，允许计算 Pedersen 哈希函数的 _pedersen\_ptr_ 需要一个 HashBuiltin，而这一参数并不存在于当前文件中，所以需要从 Cairo 函数库导入，我们要在代码中添加：

    from starkware.cairo.common.cairo_builtins import HashBuiltin
    

![](https://storage.googleapis.com/papyrus_images/e832aa4bad749ff893fe456e5ec172e7a3db89e3a39645012fd833a81f90a992.png)

![](https://storage.googleapis.com/papyrus_images/e2ab89da366ac27f463f28602e2bea7e29f90c64c8823063524b2ee8b91c7b4c.png)

成功运行！

![](https://storage.googleapis.com/papyrus_images/ebf95305748038428cb0d6096c8a2f7d22e6ad4a661fdba9988eae90d21803c2.png)

    func takes_two_arguments_and_returns_one(a: felt, b: felt) -> (c: felt):
    return (a + b)  # Do not change
    end
    

正如你所看到的，我们给 **a,b,c…** 参数传递了一个奇怪的数据类型 \*\*felt，\*\*没错！不同于 Solidity 有许多数据类型，如 uint256，字符串等，Cairo 使用的是一种被称为 felt 的数据类型（我们会在以后的文章中深入讨论这个问题）。

从我们的函数签名中，能发现我们传入了一个不被函数使用的 c 参数。这个参数就是该函数的返回值。你的返回值通过 _\->_ 来指定。

![](https://storage.googleapis.com/papyrus_images/ea2df448db1698445971ccc77197cc97cec8c785d47ad880fee90bd6870cccde.png)

![](https://storage.googleapis.com/papyrus_images/84f026575d8e990363f735570222ba523dc1bef73ab29b1adecb5883d025f0e0.png)

Cairo 命名空间是在合约中使用模块的一种非常强大的方式。当从多个模块导入时，它允许你在一个标识符下定义函数范围，有助于防止冲突。它还有助于提高代码可读性。

在 Cairo 执行一个命名空间，要使用 namespace 关键字，为命名空间指定一个名字：

    namespace starklings
        func starkling_exercise(a: felt, b:felt) -> (c: felt):
           return (a+b)
        end
    end
    

从文件中使用命名空间中的函数：

    starklings.starkling_exercise(3, 5)
    

为了通过执行了命名空间的测试，需要在命名空间 _my\_namespace_ 内调用 _return\_something_ 函数。

![](https://storage.googleapis.com/papyrus_images/9246ea2fedf9c5d4f4fd0c7b10990d766e904c2ec9a3a3259818c931628018f6.png)

![](https://storage.googleapis.com/papyrus_images/9dbdabfbe912ead1cfa5072588316da60714276271ec953af17b766826d0c5b9.png)

Cairo 和 Solidity 有着极度相似的结构体概念。

首先用 **struct** 关键字定义，然后用 **member** 关键字指定其属性。不同于 Solidity 的是，Cairo 中的结构体也有一个 size 属性，即其成员规模之和。

在 Cairo 中的结构体：

    struct Starklings:
       member first: felt
       member second: felt
    end
    

为了通过测试，我们要执行一个 Currency 结构体。

需要检查测试找出结构体的成员。调用结构体测试的第 13 行：

    local euro : Currency = Currency('Euro', 2)
    

在此，我们注意到结构体有两个成员，可能分别是名称和 ID，但是 14 行出现：

    assert euro.name = 'Euro'
    

![](https://storage.googleapis.com/papyrus_images/15a03552a29b1c971675f170fb675268ac84c8159f92850bb56c26afdb4f85b0.png)

修改结构体来通过测试：

    struct Currency:
       member name: felt
       member id: felt
    end
    

![](https://storage.googleapis.com/papyrus_images/c30f6edfbdd4bc48110009232639854dfb50e414c47dced981f7f7fee3670b36.png)

最后
--

到这里，我想你现在已经很好理解了 Cairo 的基本语法、函数参数、命名空间、结构体等。

在下一讲中，将通过**字符串挑战**，并深入了解什么是 **felt**。

如果觉得本教程对你有帮助，转发分享给其他人吧~

---

*Originally published on [Starknet 中文](https://paragraph.com/@starknet-zh/cairo-ii-starklings-cairo)*
