Funciones
Las funciones son frecuentes en el código de Cairo. Ya has visto una de las funciones más importantes del lenguaje: la función main
, que es el punto de entrada de muchos programas. También has visto la palabra clave fn
, que te permite declarar nuevas funciones.
El código de Cairo utiliza el estilo snake case como convención para los nombres de funciones y variables, en el cual todas las letras están en minúscula y los guiones bajos separan las palabras. Aquí hay un programa que contiene un ejemplo de definición de función:
fn another_function() {
println!("Another function.");
}
fn main() {
println!("Hello, world!");
another_function();
}
Definimos una función en Cairo introduciendo fn
seguido de un nombre de función y un conjunto de paréntesis. Las llaves indican al compilador dónde empieza y termina el cuerpo de la función.
Podemos llamar a cualquier función que hayamos definido introduciendo su nombre seguido de paréntesis. Dado que otra_funcion
está definida en el programa, puede ser llamada desde dentro de la función main
. Tenga en cuenta que hemos definido otra_funcion
antes de la función main
en el código fuente; también podríamos haberla definido después también. A Cairo no le importa dónde definas tus funciones, sólo que estén definidas en algún lugar en un ámbito que pueda ser visto por quien las llame.
Empecemos un nuevo proyecto con Scarb llamado functions para explorar las funciones más a fondo. Coloque el ejemplo another_function
en src/lib.cairo y ejecútelo. Usted Debería ver la siguiente salida:
$ scarb cairo-run
Compiling no_listing_15_functions v0.1.0 (listings/ch02-common-programming-concepts/no_listing_15_functions/Scarb.toml)
Finished `dev` profile target(s) in 4 seconds
Running no_listing_15_functions
Hello, world!
Another function.
Run completed successfully, returning []
The lines execute in the order in which they appear in the main
function. First the Hello, world!
message prints, and then another_function
is called and its message is printed.
Parámetros
Podemos definir funciones para que tengan parameters, que son variables especiales que forman parte de la firma de una función. Cuando una función tiene parámetros, puedes proporcionarle valores concretos para esos parámetros. Técnicamente, los valores valores concretos se llaman arguments, pero en la conversación informal, la gente tiende a usar las palabras parameters y arguments indistintamente para las variables en la definición de una función o los valores concretos que se pasan cuando se llama a una función.
En esta versión de another_function
añadimos un parámetro:
fn main() {
another_function(5);
}
fn another_function(x: felt252) {
println!("The value of x is: {}", x);
}
Intente ejecutar este programa; debería obtener la siguiente salida:
$ scarb cairo-run
Compiling no_listing_16_single_param v0.1.0 (listings/ch02-common-programming-concepts/no_listing_16_single_param/Scarb.toml)
Finished `dev` profile target(s) in 4 seconds
Running no_listing_16_single_param
The value of x is: 5
Run completed successfully, returning []
La declaración de another_function
tiene un parámetro llamado x. El tipo de x
se especifica cómo felt252
. Cuando pasamos 5 a another_function
, la macro println!
coloca 5 donde estaba el par de llaves que contenían a x
en el formato string.
En las firmas de función, must declarar el tipo de cada parámetro. Esta es una decisión deliberada en el diseño de Cairo: requerir anotaciones de tipo en las significa que el compilador casi nunca necesita usarlas en otra parte del código el código para averiguar a qué tipo se refiere. El compilador también puede dar mensajes de error más útiles si sabe qué tipos espera la función.
Cuando defina múltiples parámetros, separe las declaraciones de parámetros con comas, así:
fn main() {
print_labeled_measurement(5, "h");
}
fn print_labeled_measurement(value: u128, unit_label: ByteArray) {
println!("The measurement is: {value}{unit_label}");
}
Este ejemplo crea una función llamada print_labeled_measurement
con dos parámetros. El primer parámetro se llama value
y es un u128
. El segundo se llama unit_label
y es de tipo ByteArray
, el tipo interno de Cairo para representar literales de string. La función luego imprime texto que contiene tanto el valor como la etiqueta de unidad unit_label
.
Let’s try running this code. Replace the program currently in your functions project’s src/lib.cairo file with the preceding example and run it using scarb cairo-run
:
$ scarb cairo-run
Compiling no_listing_17_multiple_params v0.1.0 (listings/ch02-common-programming-concepts/no_listing_17_multiple_params/Scarb.toml)
Finished `dev` profile target(s) in 5 seconds
Running no_listing_17_multiple_params
The measurement is: 5h
Run completed successfully, returning []
Dado que llamamos a la función con 5
como el valor para valor y "h"
como el valor para unit_label
, la salida del programa contiene esos valores.
Named Parameters
En Cairo, los parámetros con nombre permiten especificar los nombres de los argumentos cuando se llama a una función. Esto hace que las llamadas a funciones sean más legibles y autodescriptivas. Si quieres usar parámetros con nombre, necesitas especificar el nombre del parámetro y el valor que quieres pasarle. La sintaxis es parameter_name: value
. Si pasas una variable que tiene el mismo nombre que el parámetro, puedes escribir simplemente :parameter_name
en lugar de parameter_name: variable_name
.
Aquí un ejemplo:
fn foo(x: u8, y: u8) {}
fn main() {
let first_arg = 3;
let second_arg = 4;
foo(x: first_arg, y: second_arg);
let x = 1;
let y = 2;
foo(:x, :y)
}
Declaraciones y Expresiones
Los cuerpos de las funciones están compuestos por una serie de sentencias que terminan opcionalmente en una expresión. Hasta ahora, las funciones que hemos cubierto no han incluido una expresión final, pero ya has visto una expresión como parte de una sentencia. Como Cairo es un lenguaje basado en expresiones, esta es una distinción importante que debemos entender. Otros lenguajes no tienen las mismas distinciones, así que veamos qué son las sentencias y expresiones y cómo sus diferencias afectan los cuerpos de las funciones.
- Declaraciones son instrucciones que realizan alguna acción y no devuelven un valor.
- Expresiones se evalúan a un valor resultante. Veamos algunos ejemplos.
De hecho, ya hemos utilizado declaraciones y expresiones. Crear una variable y asignarle un valor con la palabra clave let
es una declaración. En el Listado 2-1, let y = 6;
es una declaración.
fn main() {
let y = 6;
}
Las definiciones de funciones también son sentencias; todo el ejemplo anterior es una sentencia en sí misma.
Las declaraciones no devuelven valores. Por lo tanto, no se puede asignar una sentencia let
a otra variable, como intenta hacer el siguiente código; se producirá un error:
fn main() {
let x = (let y = 6);
}
Cuando ejecutes este programa, el error que obtendrás se verá así:
$ scarb cairo-run
Compiling no_listing_18_statements_dont_return_values v0.1.0 (listings/ch02-common-programming-concepts/no_listing_20_statements_dont_return_values/Scarb.toml)
error: Missing token TerminalRParen.
--> listings/ch02-common-programming-concepts/no_listing_20_statements_dont_return_values/src/lib.cairo:3:14
let x = (let y = 6);
^
error: Missing token TerminalSemicolon.
--> listings/ch02-common-programming-concepts/no_listing_20_statements_dont_return_values/src/lib.cairo:3:14
let x = (let y = 6);
^
error: Missing token TerminalSemicolon.
--> listings/ch02-common-programming-concepts/no_listing_20_statements_dont_return_values/src/lib.cairo:3:23
let x = (let y = 6);
^
error: Skipped tokens. Expected: statement.
--> listings/ch02-common-programming-concepts/no_listing_20_statements_dont_return_values/src/lib.cairo:3:23
let x = (let y = 6);
^^
warn[E0001]: Unused variable. Consider ignoring by prefixing with `_`.
--> listings/ch02-common-programming-concepts/no_listing_20_statements_dont_return_values/src/lib.cairo:3:9
let x = (let y = 6);
^
warn[E0001]: Unused variable. Consider ignoring by prefixing with `_`.
--> listings/ch02-common-programming-concepts/no_listing_20_statements_dont_return_values/src/lib.cairo:3:18
let x = (let y = 6);
^
error: could not compile `no_listing_18_statements_dont_return_values` due to previous error
error: `scarb metadata` exited with error
La declaración let y = 6
no devuelve un valor, por lo que no hay nada a lo que x
pueda enlazar. Esto es diferente de lo que sucede en otros lenguajes, como C y Ruby, donde la asignación devuelve el valor de la asignación. En esos lenguajes, puedes escribir x = y = 6
y tanto x
como y
tendrán el valor 6
; esto no es así en Cairo.
Expressions evaluate to a value and make up most of the rest of the code that you’ll write in Cairo. Consider a math operation, such as 5 + 6
, which is an expression that evaluates to the value 11
. Expressions can be part of statements: in Listing 2-1, the 6
in the statement let y = 6;
is an expression that evaluates to the value 6
.
Calling a function is an expression since it always evaluates to a value: the function's explicit return value, if specified, or the 'unit' type ()
otherwise.
A new scope block created with curly brackets is an expression, for example:
fn main() {
let y = {
let x = 3;
x + 1
};
println!("The value of y is: {}", y);
}
Esta expresión:
let y = {
let x = 3;
x + 1
};
Este bloque de código, en este caso, se evalúa como 4. Ese valor se asigna a y como parte de la declaración let. Ten en cuenta que la línea x + 1 no tiene un punto y coma al final, lo que es diferente a la mayoría de las líneas que has visto hasta ahora. Las expresiones no incluyen un punto y coma al final. Si agregas un punto y coma al final de una expresión, la conviertes en una declaración, y en ese caso no se devolverá ningún valor. Tenlo en cuenta mientras exploras los valores de retorno de las funciones y las expresiones a continuación.
Funciones con Valores de Retorno
Las funciones pueden devolver valores al código que las llama. No nombramos los valores de retorno, pero debemos declarar su tipo después de una flecha (->
). En Cairo, el valor de retorno de la función es sinónimo del valor de la última expresión en el bloque del cuerpo de una función. Puede salir temprano de una función usando la palabra clave return
y especificando un valor, pero la mayoría de las funciones devuelven la última expresión implícitamente. Aquí hay un ejemplo de una función que devuelve un valor:
fn five() -> u32 {
5
}
fn main() {
let x = five();
println!("The value of x is: {}", x);
}
No hay llamadas a funciones ni declaraciones let
en la función five
, solo el número 5
por sí mismo. Esa es una función perfectamente válida en Cairo. Observa que se especifica el tipo de retorno de la función como -> u32
. Intenta ejecutar este código; la salida debería verse así:
$ scarb cairo-run
Compiling no_listing_20_function_return_values v0.1.0 (listings/ch02-common-programming-concepts/no_listing_22_function_return_values/Scarb.toml)
Finished `dev` profile target(s) in 4 seconds
Running no_listing_20_function_return_values
The value of x is: 5
Run completed successfully, returning []
El 5
en five
es el valor de retorno de la función, por eso el tipo de retorno es u32
. Vamos a examinar esto con más detalle. Hay dos partes importantes: en primer lugar, la línea let x = five();
muestra que estamos usando el valor de retorno de una función para inicializar una variable. Debido a que la función five
devuelve un 5
, esa línea es lo mismo que:
let x = 5;
Segundo, la función five
no tiene parámetros y define el tipo del valor de retorno, pero el cuerpo de la función es un solitario 5
sin punto y coma porque es una expresión cuyo valor queremos devolver. Veamos otro ejemplo:
fn main() {
let x = plus_one(5);
println!("The value of x is: {}", x);
}
fn plus_one(x: u32) -> u32 {
x + 1
}
Al ejecutar este código se imprimirá x = 6
. Pero si agregamos un punto y coma al final de la línea que contiene x + 1
, cambiándola de una expresión a una declaración, obtendremos un error:
fn main() {
let x = plus_one(5);
println!("The value of x is: {}", x);
}
fn plus_one(x: u32) -> u32 {
x + 1;
}
$ scarb cairo-run
Compiling no_listing_22_function_return_invalid v0.1.0 (listings/ch02-common-programming-concepts/no_listing_24_function_return_invalid/Scarb.toml)
error: Unexpected return type. Expected: "core::integer::u32", found: "()".
--> listings/ch02-common-programming-concepts/no_listing_24_function_return_invalid/src/lib.cairo:9:28
fn plus_one(x: u32) -> u32 {
^
error: could not compile `no_listing_22_function_return_invalid` due to previous error
error: `scarb metadata` exited with error
El mensaje principal de error, Unexpected return type
, revela el problema principal con este código. La definición de la función plus_one
indica que devolverá un u32
, pero las sentencias no se evalúan a un valor, lo cual se expresa por ()
, el tipo unit. Por lo tanto, no se devuelve nada, lo que contradice la definición de la función y resulta en un error.