Funciones
- Siempre se espera una función principal,
fn main(){} - Los nombres de las funciones o variables deben ser con
snake_caseokebab-case - Hoisting, la función puede ser definida y/o llamada en cualquier lugar
fn main() {
println!("Main function");
hello_rust();
}
// Hoisting, the function can be called before being declared
fn hello_rust() {
println!("Hello, Rust");
}
Parámetros
- Argumentos (variables) que forman parte de la firma de una función
- En la firma de la función se debe declarar el tipo de cada parámetro
fn main() {
another_function(5);
person("Lucas Primero", 25, 175.0);
}
fn another_function(x: i32) {
println!("The value of x is: {x}");
}
fn person(name: &str, age: u32, height: f32) {
println!("--- Personal data ---");
println!("Name: {}, Age: {}, Height: {} cm.", name, age, height);
}
Declaraciones y Expresiones
El cuerpo de una función están formados por una serie de instrucciones que, opcionalmente, terminan en una expresión
Declaraciones (Statements)
- Las statements o declaraciones son instrucciones que realizan alguna acción y no devuelven un valor
- La definición de variables y funciones son ejemplos de declaraciones.
- La definición de una función es una declaración, pero cuando se llama a la función, esa llamada se convierte en una expresión porque devuelve un valor
- Generalmente terminan con
;, lo que las diferencia de las expresiones
Expresiones (Expressions)
- Las expresiones se evalúan y devuelven un valor resultante
- Pueden formar parte de una sentencia (statement), ser asignadas a variables o usarse dentro de otras expresiones.
- Llamar a una función, una macro o ejecutar un bloque de código son ejemplos de expresiones
- No terminan con
;ni requierenreturnpara devolver un valor - Si se incluye punto y coma al final, se convierte en un statement y por lo tanto no devolverá un valor
- Las estructuras de control como
if,matchy bloques{}pueden ser expresiones.
Ejemplos
- Un bloque de código se evalúa y devuelve un valor asignado a
y
fn main() {
let y = {
let x = 5;
x * 2 // Esta es la expresión que se evalúa y devuelve 10
};
println!("The value of y is: {y}"); // The value of y is: 10
}
- El resultado de un
ifpuede asignarse a una variable
fn main() {
let temperature = 15;
let weather = if temperature > 25 { "hot" } else { "cold" };
println!("The weather is {weather}"); // The weather is cold
}
- El cuerpo de una función en Rust puede consistir en una sola expresión, sin necesidad de
return
// Definir la función es una declaración
fn square(n: i32) -> i32 {
n * n // Expresión que devuelve el resultado
}
fn main() {
// Statement: define una variable, pero no devuelve un valor
let num = 4;
// Llamar a la función es una expresión, porque devuelve un valor
let result = square(num);
println!("The square of {num} is {result}"); // The square of 4 is 16
}
fn main() {
let weight = 70.0;
let height = 1.80;
let bmi = calculate_bmi(weight, height);
println!("El BMI es: {:.2}", bmi); // {:.2} para mostrar solo 2 decimales
}
// BMI = height(kg)/height(m)^2
fn calculate_bmi(weight_kg: f64, height_m: f64) -> f64 {
// expression dentro del cuerpo de la función (statement)
weight_kg / (height_m * height_m)
}
Funciones con valores de retorno
- Se usa
-> Typepara indicar el tipo de dato que devuelve la función - No es obligatorio usar la palabra
return, ya que la última expresión dentro de la función se considera el valor de retorno - La última expresión del cuerpo de la función no debe terminar con
;, ya que convertiría la expresión en una declaración sin valor de retorno - Se puede salir de forma temprana de una función utilizando
returny especificando un valor o no - Además de tipos primitivos (
i32,f64,bool, etc.), las funciones pueden devolver tipos más complejos como tuplas(x, y, z),Option<T>yResult<T, E>,
Retorno implícito
fn main() {
let x = five();
println!("The value of x is: {x}");
}
fn five() -> i32 {
5
}
fn main() {
let result = add(5, 3);
println!("5+3: {}", result);
println!("1+2: {}", add(1, 2));
}
fn add(a: i32, b: i32) -> i32 {
a + b
}
Retorno temprano con return
fn check_number(n: i32) -> &'static str {
if n < 0 {
return "Negative"; // Retorno temprano
}
"Positive or zero" // Última expresión, retorna implícitamente
}
fn main() {
println!("{}", check_number(-5)); // Imprime: Negative
println!("{}", check_number(10)); // Imprime: Positive or zero
}
fn divide(a: f64, b: f64) -> Option<f64> {
if b == 0.0 {
return None; // Salida inmediata si b es 0
}
Some(a / b) // Última expresión, se devuelve automáticamente
}
Devolviendo tuplas (x, y, z)
fn calculate_circle(radius: f64) -> (f64, f64) {
let area = std::f64::consts::PI * radius * radius;
let circumference = 2.0 * std::f64::consts::PI * radius;
(area, circumference)
}
fn main() {
let radius = 5.0;
let (area, circumference) = calculate_circle(radius);
println!("Área: {:.2}, Circunferencia: {:.2}", area, circumference);
}
Devolviendo Option<T> para valores opcionales
fn divide(a: f64, b: f64) -> Option<f64> {
if b == 0.0 {
None // Retorna `None` si la división no es válida
} else {
Some(a / b) // Retorna `Some(valor)` si la división es válida
}
}
fn main() {
// Accediento a un Option<T> con match
match divide(10.0, 2.0) {
Some(result) => println!("Resultado: {result}"),
None => println!("No se puede dividir por cero"),
}
// Accediento a un Option<T> con if
if let Some(res) = divide(10.0, 0.0){
println!("Resultado: {}", res);
} else {
println!("No se pudo dividir");
}
}
Devolviendo Result<T, E> para manejo de errores
fn safe_divide(a: f64, b: f64) -> Result<f64, &'static str> {
if b == 0.0 {
// Devuelve un error si `b` es 0
Err("Error: división por cero")
} else {
// Devuelve el resultado si es válido
Ok(a / b)
}
}
fn main() {
match safe_divide(10.0, 2.0) {
Ok(result) => println!("Resultado: {result}"),
Err(error) => println!("{error}"),
}
}