Programación Funcional

La programación funcional es el segundo paradigma de programación más popular después de la programación orientada a objetos. Durante muchos años, estos dos paradigmas se han separado en diferentes lenguajes para evitar mezclarlos. Los lenguajes de múltiples paradigmas han intentado soportar ambos enfoques. Rust es uno de esos lenguajes.

Como definición amplia, la programación funcional enfatiza el uso de funciones compatibles y altamente reutilizables para definir el comportamiento del programa. Utilizando estas técnicas, mostraremos cómo la programación funcional ha adaptado soluciones ingeniosas a muchos problemas comunes pero difíciles.

Daremos un breve recorrido sobre como utilizar el estilo funcional para reducir el peso y la complejidad del código, así tendrás un panorama general de las técnicas de la programación funcional. Asumo que estás familiarizado al menos con los primeros 10 capítulos de la documentación oficial de Rust, sin embargo haré énfasis en la sintaxis básica cuando sea necesario para profundizar en temas avanzados de la programación funcional.

Reducción del peso y la complejidad del código

La programación funcional puede reducir considerablemente la cantidad y la complejidad del código necesario para realizar tareas. Genera un código mas conciso, componetizable y ligero de ejecutar.

En Rust, la aplicación adecuada de los principios funcionales, puede simplificar los requisitos de diseño que a menudo son complejos, y hacer que la programación sea una experiencia mucho más productiva y gratificante.

Hacer que los genéricos sean más genéricos

Los genéricos se relacionan con la práctica de parametrizar estructuras de datos y funciones que se originó en lenguajes funcionales. En Rust, y en otros lenguajes, esto se conoce como genéricos. Los tipos y las funciones pueden ser parametrizados.

En la programación funcional se emplea el uso de tipos genéricos. Las estructuras de datos y las funciones son parametrizadas con tipos genéricos, y se pueden colocar una o más restricciones para indicar los requisitos de un trait o lifetimes.

Traits Bounds

El "trait bound" es un concepto utilizado en lenguajes de programación como Rust para establecer restricciones en los tipos genéricos. Un "trait" en Rust es similar a una interfaz en otros lenguajes y define un conjunto de métodos que un tipo debe implementar.

Cuando se utiliza un tipo genérico en una función o estructura, se puede especificar un "trait bound" para limitar los tipos que pueden usarse como argumentos genéricos. Esto significa que solo se pueden utilizar tipos que implementen el trait especificado.

Por ejemplo, si tenemos una función genérica que toma un argumento de tipo T y queremos asegurarnos de que T sea un tipo que implemente el trait "Clone", podemos especificar el trait bound de la siguiente manera:

fn clone_and_print<T: Clone>(value: T) {
    let cloned_value = value.clone();
    println!("Cloned value: {:?}", cloned_value);
}

Las definiciones de estructuras pueden volverse redundantes sin los genéricos. Aquí se muestra la definición de tres estructuras que definen un concepto común de un punto. Sin embargo, las estructuras utilizan diferentes tipos numéricos, por lo que el concepto singular se expande en tres definiciones de tipos separadas para el mismo punto.

En este caso, el trait bound ": Clone" indica que el tipo genérico T debe implementar el trait "Clone". Esto garantiza que se puede realizar una clonación del valor pasado como argumento.

El uso de trait bounds permite establecer restricciones en los tipos genéricos, lo que brinda más seguridad en tiempo de compilación y ayuda a evitar errores al utilizar los tipos genéricos en funciones o estructuras.

Lifetime bounds