Funciones genéricas
Parte de la sección Programación Orientada a Objetos del Journey de Rust de Coddy. Lección 27 de 61.
Los genéricos no se limitan a las estructuras. También puedes escribir funciones independientes que funcionen con cualquier tipo. Esto resulta útil cuando necesitas una función de utilidad que no pertenezca a una estructura específica, pero que siga siendo flexible.
La sintaxis refleja lo que has visto con las estructuras. Declara el parámetro genérico entre corchetes angulares después del nombre de la función, y después úsalo en los parámetros y en el tipo de retorno:
fn identity<T>(value: T) -> T {
value
}
Esta función identity toma un valor de cualquier tipo y lo devuelve sin cambios. <T> declara el genérico, value: T lo acepta como parámetro y -> T especifica el tipo de retorno. Cuando llamas a la función, Rust infiere el tipo concreto:
let num = identity(42); // T es i32
let text = identity("hello"); // T es &str
También puedes usar múltiples parámetros genéricos en las funciones, igual que con las estructuras:
fn make_pair<T, U>(first: T, second: U) -> (T, U) {
(first, second)
}
let pair = make_pair(10, "ten"); // devuelve (i32, &str)
Las funciones genéricas te permiten escribir una lógica reutilizable una sola vez y aplicarla a muchos tipos, reduciendo la duplicación de código y manteniendo la seguridad de tipos.
Desafío
Fácil¡Construyamos un módulo de utilidades con funciones genéricas que puedan trabajar con cualquier tipo! Crearás funciones independientes que demuestran cómo los genéricos hacen que tu código sea flexible y reutilizable sin estar ligado a una estructura específica.
Organizarás tu código en dos archivos:
utils.rs: Crea una colección de funciones de utilidad genéricas públicas:wrap_in_pair<T>: recibe un único valor y devuelve una tupla que contiene ese valor dos veces:(value, value). Esto requiere que el tipo implementeClone, así que usa<T: Clone>swap<T, U>: recibe dos valores de tipos potencialmente diferentes y los devuelve en orden inverso como una tupla(U, T)
main.rs: Importa tu módulo de utilidades y demuestra estas funciones genéricas trabajando con diferentes tipos. Muestra cómo las mismas definiciones de funciones gestionan enteros, números de punto flotante y cadenas sin problemas.
En tu archivo principal, demuestra tus funciones de utilidad:
- Usa
wrap_in_paircon un entero (la primera entrada, analizada comoi32) e imprime ambos elementos - Usa
wrap_in_paircon una cadena (la segunda entrada) e imprime ambos elementos - Usa
swapcon un entero (la tercera entrada, analizada comoi32) y una cadena (la cuarta entrada); después, imprime el resultado intercambiado
Tu salida debe seguir este formato:
Pair of ints: ({value}, {value})
Pair of strings: ({value}, {value})
Swapped: ({string}, {int})Por ejemplo, con las entradas 5, hello, 42 y world:
Pair of ints: (5, 5)
Pair of strings: (hello, hello)
Swapped: (world, 42)Observa cómo wrap_in_pair funciona de forma idéntica tanto para enteros como para cadenas, y swap gestiona dos tipos completamente diferentes: ¡esa es la flexibilidad de las funciones genéricas!
Recibirás cuatro entradas: un entero, una cadena, otro entero y otra cadena.
Pruébalo tú mismo
mod utils;
use std::io;
fn main() {
// Lee las cuatro entradas
let mut input1 = String::new();
io::stdin().read_line(&mut input1).expect("Failed to read line");
let num1: i32 = input1.trim().parse().expect("Invalid integer");
let mut input2 = String::new();
io::stdin().read_line(&mut input2).expect("Failed to read line");
let str1 = input2.trim().to_string();
let mut input3 = String::new();
io::stdin().read_line(&mut input3).expect("Failed to read line");
let num2: i32 = input3.trim().parse().expect("Invalid integer");
let mut input4 = String::new();
io::stdin().read_line(&mut input4).expect("Failed to read line");
let str2 = input4.trim().to_string();
// TODO: Usa wrap_in_pair con num1 e imprime el resultado
// Formato: Pair of ints: ({value}, {value})
// TODO: Usa wrap_in_pair con str1 e imprime el resultado
// Format: Pair of strings: ({value}, {value})
// TODO: Usa swap con num2 y str2, luego imprime el resultado intercambiado
// Formato: Swapped: ({string}, {int})
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación Orientada a Objetos
1Métodos y Comportamiento
Intro a Bloques de ImplementaciónEl Parámetro SelfMétodos MutablesFunciones AsociadasMúltiples Bloques de ImplementaciónEncadenamiento de MétodosResumen - Acciones de Rectángulo4Proyecto: Mascota virtual
Definir la mascotaAlimentar a la mascota7Traits estándar
El trait DebugEl trait DisplayClone y CopyTraits de igualdadResumen - Punto imprimible10Proyecto: Sistema de documentos
El Trait DrawComponente de texto2Encapsulamiento y módulos
Conceptos básicos de módulosLa palabra clave PublicCampos privadosGettersSettersRepaso - Casillero seguro5Genéricos
Estructuras genéricasMétodos genéricosMúltiples tipos genéricosFunciones genéricasResumen - Punto de coordenadas8Traits como límites
Sintaxis de límites de traitsLímites múltiplesLa cláusula whereRetornar tipos con traitsResumen: Impresora genérica11Patrones de diseño en Rust
Patrón NewtypeComposiciónEl Trait DropFrom e IntoResumen - Mock de Smart Pointer3Enums avanzados
Enums con datosMétodos en EnumsCoincidencia de variantes de datosEl Enum Option revisitadoResumen - Enum Shape6Definición de Traits
¿Qué es un Trait?Implementación de TraitsImplementaciones por defectoSobrescribir valores por defectoTraits con parámetrosResumen - Reproductor multimediaPractica por tu cuenta: Compilador de Rust online