#函数

函数在Cairo代码中相当常见。您已经看到了语言中最重要的函数之一:main函数,它是许多程序的入口点。您也看到了fn关键字,它允许您声明新函数。

在Cairo代码中,通常将蛇式命名法作为函数和变量名称的惯用风格,其中所有字母都是小写的,下划线分隔单词。以下是包含示例函数定义的程序:

use debug::PrintTrait;

fn another_function() { 'Another function.'.print(); }

fn main() { 'Hello, world!'.print(); another_function(); }

在Cairo中通过输入fn后跟函数名称和一组括号来定义函数。花括号告诉编译器函数体的开始和结束位置。我们可以通过输入其名称后跟一组括号来调用我们定义的任何函数。因为another_function在程序中已定义,所以可以从main函数中调用它。请注意,我们将another_function定义放在了主函数之前的源代码中;我们也可以在之后定义它。Cairo不关心您定义函数的位置,只要它们在调用方可见的作用域中定义即可。

参数

我们可以定义具有参数的函数,这些参数是函数签名的一部分的特殊变量。当一个函数具有参数时,可以为其提供这些参数的具体值。从技术上讲,具体值称为参数,但在日常会话中,人们倾向于互换使用参数和参数这两个词,无论是函数定义中的变量还是在调用函数时传递的具体值。

在这个版本的another_function中,我们添加了一个参数:

use debug::PrintTrait;

fn main() { another_function(5); }

fn another_function(x: felt252) { x.print(); }

another_function的声明有一个名为x的参数。 x的类型指定为felt252。当我们向another_function传递5时,.print()函数在控制台中输出5。

在函数签名中,必须声明每个参数的类型。这是Cairo设计中的一个有意决策:在函数定义中要求类型注释意味着编译器几乎永远不需要在代码的其他地方使用它们来确定您所需的类型。如果编译器知道函数期望什么类型,它还能够提供更有用的错误消息。 在定义多个参数时,使用逗号分隔参数声明,例如:

use debug::PrintTrait;

fn main() { another_function(5, 6); }

fn another_function(x: felt252, y: felt252) { x.print(); y.print(); }

该例子创建了一个名为another_function的函数,并带有两个参数。第一个参数命名为x,是一个felt252。第二个参数命名为y,类型也为felt252。然后函数打印出了felt x和然后是felt y的内容。

尝试运行此代码。用前面的示例替换functions项目的src / lib.cairo文件,并使用cairo-run src / lib.cairo运行它:因为我们使用了5作为x的值,6作为y的值来调用函数,因此程序输出包含这些值。

语句和表达式

函数体由一系列语句组成,并以一个可选的表达式结束。到目前为止,我们涵盖的函数没有包含一个结束表达式,但是您已经在一句话中看到了一个表达式。因为Cairo是一种基于表达式的语言,所以了解这一点很重要。其他语言没有相同的区别,因此让我们看看语句和表达式是什么,以及它们的差异如何影响函数体。

•语句是执行某些操作并不返回值的指令。 •表达式求值为结果值。让我们来看一些例子。

实际上,我们已经使用了语句和表达式。使用let关键字创建变量并将其赋值是一个语句。在2-1清单中,let y = 6;是一条语句。

fn main() { let y = 6; }

函数定义也是语句;整个前面的例子本身就是一个语句。

语句不返回值。因此,您不能将let语句分配给另一个变量,正如以下代码尝试的那样;您将收到一个错误:

fn main() { let x = (let y = 6); }

当运行这个程序时,您会收到以下错误:

error: Missing token TerminalRParen. --> src/lib.cairo:2:14 let x = (let y = 6); ^

error: Missing token TerminalSemicolon. --> src/lib.cairo:2:14 let x = (let y = 6); ^

error: Missing token TerminalSemicolon. --> src/lib.cairo:2:14 let x = (let y = 6); ^

error: Skipped tokens. Expected: statement. --> src/lib.cairo:2:14 let x = (let y = 6);

let y = 6语句不返回值,因此没有任何东西可以绑定到x。这与其他语言(例如C和Ruby)的情况不同,其中分配返回分配的值。在这些语言中,您可以编写x = y = 6,并且x和y都具有值6;这在Cairo中不是这种情况。

表达式求值为一个值,并占据了你在Cairo中编写的大部分代码。考虑一个数学运算,例如5 + 6,这是一个表达式,其评估为值11。表达式可以是语句的一部分:在2-1清单中,在语句let y = 6;中的6是一个表达式,其评估为值6。调用函数是一种表达式。用花括号创建的新作用域块是一个表达式,例如:

use debug::PrintTrait; fn main() { let y = { let x = 3; x + 1 };

y.print();

}

这个表达式:

{ let x = 3; x + 1 }

是一个块,它在这种情况下计算为4。该值将作为let语句的一部分绑定到y。请注意,x + 1行没有分号结尾,这与您看到的大多数行不同。表达式不包括结束分号。如果在表达式的末尾添加分号,它将变成语句,然后它将不返回值。在您探索函数返回值和表达式之后,请记住这一点。

带返回值的函数

函数可以向调用它们的代码返回值。我们不给返回值命名,但必须在箭头(->)后声明其类型。在Cairo中,函数的返回值与函数体块中的最后一个表达式的值是同义的。您可以这是一些使用Cairo语言编写的代码。第一个程序中定义了一个返回5的函数,然后在main函数中调用该函数以初始化变量x,最终输出结果为5。第二个程序中定义了一个名称为plus_one的函数,它接受一个u32类型的参数,并返回将该参数加1后的结果。在main函数中,我们使用plus_one(5)来初始化变量x,并输出x的值,该值是6。但是,如果我们在函数中将x + 1的末尾加上分号,我们将得到编译错误,因为这会使该语句成为语句而非表达式,导致函数返回类型不匹配的错误。在这种情况下,编译器希望函数返回一个u32类型的值,但实际返回了一个unit类型的值(()),这与函数定义不符,导致出现错误。