¡Bienvenidos de nuevo a Rustaceo! Continuando con nuestra serie sobre Rust y Pokémon, hoy exploraremos los tipos de datos y las funciones en Rust. Estos son los bloques de construcción fundamentales que te permitirán crear programas más complejos y efectivos. Acompáñame mientras descubrimos cómo utilizar estos conceptos con ejemplos del mundo Pokémon.
Tipos de datos en Rust #
Rust es un lenguaje de tipo estático, lo que significa que el tipo de cada variable debe ser conocido en tiempo de compilación. Esto ayuda a prevenir errores y garantiza la seguridad del código.
Tipos escalares #
Los tipos escalares representan un solo valor. En Rust, tenemos cuatro tipos escalares principales:
- Enteros (
i8
,u8
,i16
,u16
, etc.): Números sin punto decimal. - Puntos Flotantes (
f32
,f64
): Números con punto decimal. - Booleanos (
bool
): Valores verdaderos o falsos. - Caracteres (
char
): Representa un solo carácter Unicode.
Ejemplo:
let nivel_item1: u8 = 25; // Entero sin signo
let peso_snorlax: f32 = 460.0; // Punto flotante de 32 bits
let es_legendario: bool = false; // Valor booleano
let inicial: char = 'P'; // Carácter
Tipos compuestos #
Los tipos compuestos pueden agrupar múltiples valores en un solo tipo. Los dos tipos compuestos principales en Rust son:
- Tuplas (
tuple
): Agrupan valores de diferentes tipos. - Arreglos (
array
): Colección de valores del mismo tipo y tamaño fijo.
Ejemplo de Tupla:
let item1: (&str, u8, f32) = ("Item1", 25, 6.0);
// (Nombre, Nivel, Peso)
Ejemplo de Arreglo:
let equipo: [&str; 3] = ["Item4", "Item2", "Item3"];
Usando estructuras para definir tipos personalizados #
Las estructuras (structs
) te permiten crear tipos de datos más complejos.
Ejemplo: Definir una Estructura para un Pokémon
struct Pokémon {
nombre: String,
tipo: String,
nivel: u8,
salud: u16,
}
let item4 = Pokémon {
nombre: String::from("Item4"),
tipo: String::from("Planta/Veneno"),
nivel: 5,
salud: 45,
};
println!("{} es de tipo {} y está en el nivel {}.", item4.nombre, item4.tipo, item4.nivel);
Enums: tipos personalizados con variantes #
Los enums te permiten definir un tipo que puede ser uno de varios variantes.
Ejemplo: Estados de un Pokémon
enum Estado {
Saludable,
Envenenado,
Paralizado,
Dormido,
Confundido,
}
let estado_actual = Estado::Envenenado;
match estado_actual {
Estado::Saludable => println!("El Pokémon está en perfectas condiciones."),
Estado::Envenenado => println!("El Pokémon está envenenado."),
_ => println!("El Pokémon tiene un estado especial."),
}
Funciones en Rust #
Las funciones son bloques de código reutilizables que realizan tareas específicas.
Sintaxis Básica:
fn nombre_funcion(parametros) -> TipoDeRetorno {
// Cuerpo de la función
}
Ejemplo: función para calcular el daño de un ataque #
fn calcular_dano(ataque: u16, defensa: u16, poder: u16) -> u16 {
let dano = ((ataque as f32 / defensa as f32) * poder as f32 / 50.0 + 2.0) as u16;
dano
}
let dano_causado = calcular_dano(55, 40, 60);
println!("El daño causado es {} puntos.", dano_causado);
En este ejemplo:
- La función
calcular_dano
toma tres parámetros:ataque
,defensa
ypoder
. - Calcula el daño basado en una fórmula simplificada.
- Retorna el daño como un
u16
.
Funciones con parámetros y retornos #
Las funciones pueden tener múltiples parámetros y pueden retornar valores.
Ejemplo: Función para Atrapar un Pokémon
fn atrapar_elemento(pokeball: &str, probabilidad: f32) -> bool {
if probabilidad >= 0.5 {
println!("¡Has atrapado al Pokémon usando una {}!", pokeball);
true
} else {
println!("El Pokémon escapó de la {}.", pokeball);
false
}
}
let exito = atrapar_elemento("Poké Ball", 0.7);
Closures: funciones anónimas #
Los closures son funciones anónimas que pueden capturar variables del entorno.
Ejemplo: Ordenar una Lista de Pokémon por Nivel
let mut elemento_equipo = vec![
("Item1", 25),
("Item2", 15),
("Item4", 20),
];
elemento_equipo.sort_by(|a, b| a.1.cmp(&b.1));
println!("Equipo ordenado por nivel: {:?}", elemento_equipo);
En este closure:
|a, b| a.1.cmp(&b.1)
es una función anónima que compara el segundo pokémon (nivel) de las tuplas.
Uso de generics en funciones #
Los generics permiten que tus funciones trabajen con diferentes tipos.
Ejemplo: Función Genérica para Mostrar Información
fn mostrar_info<T: std::fmt::Debug>(pokemon: T) {
println!("{:?}", pokemon);
}
mostrar_info(item4);
mostrar_info(equipo);
Aquí, mostrar_info
puede aceptar cualquier tipo que implemente el trait Debug
.
Práctica: simulando una batalla pokémon #
Pongamos en práctica lo aprendido creando funciones y utilizando tipos de datos.
struct Pokémon {
nombre: String,
tipo: String,
nivel: u8,
salud: i16,
ataque: u16,
defensa: u16,
}
fn ataque_elemento(atacante: &Pokémon, defensor: &mut Pokémon, poder: u16) {
let dano = calcular_dano(atacante.ataque, defensor.defensa, poder);
defensor.salud -= dano as i16;
println!(
"{} ataca a {} y causa {} puntos de daño.",
atacante.nombre, defensor.nombre, dano
);
}
fn main() {
let item1 = Pokémon {
nombre: String::from("Item1"),
tipo: String::from("Eléctrico"),
nivel: 25,
salud: 100,
ataque: 55,
defensa: 40,
};
let mut item3 = Pokémon {
nombre: String::from("Item3"),
tipo: String::from("Agua"),
nivel: 20,
salud: 100,
ataque: 48,
defensa: 65,
};
ataque_elemento(&item1, &mut item3, 50);
println!(
"La salud de {} ahora es {}.",
item3.nombre, item3.salud
);
}
En este programa:
- Definimos una estructura
Pokémon
con varios atributos. - Creamos una función
ataque_elemento
que simula un ataque entre dos Pokémon. - Usamos referencias y mutabilidad para modificar el estado del defensor.
- Ejecutamos una batalla simple y mostramos los resultados.
Conclusión #
Los tipos de datos y las funciones son esenciales para cualquier programa en Rust. Entender cómo definir y utilizar diferentes tipos te permite modelar datos complejos, mientras que las funciones te ayudan a organizar y reutilizar el código.
Al usar ejemplos de Pokémon, hemos visto cómo estos conceptos se aplican en situaciones similares a las que podrías enfrentar en desarrollo real. Te animo a que sigas experimentando y creando tus propias funciones y estructuras.
¿Disfrutaste este artículo? ¡Intenta crear tus propios Pokémon y simula batallas entre ellos! Si tienes dudas o quieres compartir tus creaciones, ¡deja un comentario abajo!
Próxima vez en Rustaceo: Nos adentraremos en “Control de Flujo en Rust: Tomando Decisiones”, continuando nuestra aventura en el mundo de Rust y Pokémon.
¡Hasta la próxima, entrenadores!