ProgramaciónDesarrollador Backend

¿Cómo funcionan los punteros inteligentes en Rust (Box, Rc, Arc, RefCell)? ¿En qué se diferencian y en qué casos conviene elegir cada uno?

Supere entrevistas con el asistente de IA Hintsage

Respuesta

En Rust no hay un recolector de basura clásico, por lo que se utilizan punteros inteligentes (smart pointers) para gestionar la propiedad de estructuras complejas. Los más comunes son:

  • Box<T> — asigna memoria para un objeto en el heap y transfiere la propiedad de esta. Se utiliza en casos donde el tamaño de los datos no se conoce en tiempo de compilación o se requiere un recurso único pero movible.

  • Rc<T> (Reference Counted) — conteo de referencias, permite que varias variables "compartan" la propiedad de datos inmutables (solo en un contexto de un solo hilo).

  • Arc<T> (Atomic Reference Counted) — también implementa el conteo de referencias, pero de forma atómica; es adecuado para programas multihilo.

  • RefCell<T> — proporciona propiedad "mutable interna" en tiempo de ejecución, permitiendo cambiar el contenido incluso a través de una referencia inmutable, pero solo en un hilo (¡no es seguro para hilos!).

Ejemplo:

use std::rc::Rc; let a = Rc::new(vec![1,2,3]); let b = Rc::clone(&a); // Ahora tanto a como b son propietarios de los mismos datos

Pregunta trampa

¿Se puede usar Rc<T> en código multihilo si todos los hilos solo leen datos? Explique.

Respuesta: ¡No, no se puede! A pesar de que Rc<T> permite solo acceso inmutable a los datos, el propio contenedor Rc<T> no es seguro para hilos, ya que el conteo interno de referencias no está protegido contra condiciones de carrera. Para esto está destinado Arc<T> — su contador interno es seguro para hilos.

Ejemplo:

// ¡El siguiente código no se compilará! use std::thread; use std::rc::Rc; let five = Rc::new(5); for _ in 0..10 { let five = Rc::clone(&five); thread::spawn(move || { println!("{}", five); }); }

Ejemplos de errores reales debido al desconocimiento de los detalles del tema


Historia

Se intentó utilizar Rc<T> para compartir una caché entre hilos para acelerar un servicio web. En producción se obtuvieron errores extraños y datos corruptos. Después de investigar, se descubrió que Rc no es seguro para hilos y el contador de referencias se había dañado. Solución: cambio a Arc<T>.

Historia

En una aplicación de escritorio, un gran árbol de objetos se almacenó en Box<T>, pero no se tuvo en cuenta que varias partes de la interfaz de usuario necesitaban compartir la propiedad de los datos. Esto llevó a errores de compilación. La solución fue utilizar Rc<T> para compartir el acceso.

Historia

En un módulo de lógica de negocio, se utilizó RefCell<T> para organizar el acceso mutable a datos que también se pasaban a través de Arc<T> entre hilos. Pero intentar combinar RefCell<T> y Arc<T> llevó a condiciones de carrera y pánicos en tiempo de ejecución. Para una opción segura para hilos, se debió utilizar Mutex<T> o RwLock<T> en lugar de RefCell<T>.