Algoritmos y estructuras de datos en Rust: guía práctica

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Introducción a algoritmos y estructuras de datos en Rust



El lenguaje de programación Rust ha ganado reconocimiento entre los desarrolladores gracias a su alto rendimiento y estrictas garantías de seguridad de memoria. Sin embargo, la transición a Rust desde otros lenguajes como Python o JavaScript puede ser compleja, especialmente al implementar algoritmos y estructuras de datos clásicos. En este artículo, exploraremos cómo implementar eficientemente estructuras de datos básicas y algoritmos en Rust, teniendo en cuenta las particularidades del sistema de ownership y borrowing.



El artículo está dirigido a desarrolladores que ya conocen los fundamentos de Rust. Nos centraremos en ejemplos prácticos de código que se pueden usar inmediatamente en proyectos reales. Todos los ejemplos incluirán comentarios detallados.



Implementación de una lista enlazada en Rust



La lista enlazada es una de las estructuras de datos fundamentales. En Rust, su implementación requiere especial atención debido a las reglas de ownership. Consideremos una lista simplemente enlazada usando Box para la gestión de memoria.



Lista simplemente enlazada básica



Empecemos definiendo el nodo y la lista:



use std::fmt::Display;

#[derive(Debug)]struct Node<T> { value: T, next: Option<Box<Node<T>>>,}

#[derive(Debug)]struct LinkedList<T> { head: Option<Box<Node<T>>>, length: usize,}

impl<T> LinkedList<T> { fn new() -> Self { LinkedList { head: None, length: 0, } }

fn push_front(&mut self, value: T) { let new_node = Box::new(Node { value, next: self.head.take(), }); self.head = Some(new_node); self.length += 1; }

fn pop_front(&mut self) -> Option<T> { self.head.take().map(|node| { self.head = node.next; self.length -= 1; node.value }) }

fn is_empty(&self) -> bool { self.head.is_none() }

fn len(&self) -> usize { self.length }}


Observa el uso de Option<Box<Node<T>>>. Esta es la forma estándar de crear estructuras recursivas en Rust, ya que el compilador requiere un tamaño conocido de antemano. El método take() permite extraer de forma segura un valor de Option, reemplazándolo con None.



Iteración sobre la lista



Implementemos un iterador para la lista:



impl<T: Clone> LinkedList<T> {    fn iter(&self) -> LinkedListIterator<T> {        LinkedListIterator {            current: self.head.as_deref(),        }    }}

struct LinkedListIterator<T> { current: Option<&Node<T>>,}

impl<T> Iterator for LinkedListIterator<T> { type Item = &T;

fn next(&mut self) -> Option<Self::Item> { self.current.take().map(|node| { self.current = node.next.as_deref(); &node.value }) }}


Árbol binario de búsqueda (BST) en Rust



Los árboles son otra estructura importante. Implementemos un árbol binario de búsqueda con soporte para inserción, búsqueda y recorrido.



Estructura e inserción básica



#[derive(Debug)]struct TreeNode<T> {    value: T,    left: Option<Box<TreeNode<T>>>,    right: Option<Box<TreeNode<T>>>,}

#[derive(Debug)]struct BinarySearchTree<T> { root: Option<Box<TreeNode<T>>>,}

impl<T: Ord> BinarySearchTree<T> { fn new() -> Self { BinarySearchTree { root: None } }

fn insert(&mut self, value: T) { let mut current = &mut self.root; while let Some(node) = current { if value <

Blogs

Book Recommendations