Autodesreferencia en Rust: reglas, ejemplos y "magia" bajo el capó

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

Autodesreferencia en Rust: cómo funciona y por qué no es magia

\n\n

Si alguna vez has escrito en Rust, seguramente habrás notado que el compilador permite llamar métodos sobre referencias y punteros inteligentes como si estuvieras trabajando con el valor en sí mismo. Esto no es magia ni un error, es un mecanismo estrictamente definido llamado autodesreferencia (auto-dereferencing).

\n\n

En Stack Overflow, la pregunta «What are Rust's exact auto-dereferencing rules?» ha acumulado miles de visitas, y con razón. Comprender estas reglas transforma el código en Rust de un «conjunto de errores extraños» a una herramienta elegante y predecible. Echemos un vistazo bajo el capó.

\n\n

¿Qué es la autodesreferencia y para qué sirve?

\n\n

En Rust tenemos referencias (&T, &mut T) y punteros inteligentes (Box<T>, Rc<T>, Arc<T>). Cuando escribes my_box.some_method(), el compilador aplica automáticamente la desreferencia para llegar al valor interno T. Sin este mecanismo, tendrías que escribir (*my_box).some_method() o (**my_ref).some_method(), lo que rápidamente se vuelve tedioso y ensucia el código.

\n\n

La autodesreferencia hace que el código sea limpio, pero es importante entender sus límites y reglas exactas para no caer en trampas.

\n\n

Reglas exactas de la autodesreferencia en Rust

\n\n

Analicemos la especificación oficial. Cuando el compilador encuentra una expresión como x.method(), intenta encontrar un método llamado method para el tipo T, donde T es el tipo de x. Si no se encuentra el método, el compilador inicia un proceso de desreferencia automática (o toma de referencia).

\n\n

Esta es la secuencia de pasos que sigue el compilador (simplificada):

\n\n
    \n
  1. Paso 1: Intentar encontrar el método para el tipo T (tal cual).
  2. \n
  3. Paso 2: Si no se encuentra, intentar encontrar el método para &T o &mut T (tomar referencia).
  4. \n
  5. Paso 3: Si no se encuentra, aplicar Deref, es decir, desreferenciar una vez (obtener U a partir de T).
  6. \n
  7. Paso 4: Repetir los pasos 1-3 para el nuevo tipo U.
  8. \n
  9. Paso 5: Si después de varias desreferencias el método aún no se encuentra, error de compilación.
  10. \n
\n\n

Importante: el compilador no hace una cadena infinita. Aplica la desreferencia hasta que encuentra el método o llega a un tipo que no implementa Deref.

\n\n

Ejemplos: de lo simple a lo complejo

\n\n

Veamos código que demuestra estas reglas en acción.

\n\n

Ejemplo 1: Referencia básica

\n\n
struct Point {\n    x: i32,\n    y: i32,\n}\n\nimpl Point {\n    fn distance_from_origin(&self) -> f64 {\n        ((self.x.pow(2) + self.y.pow(2)) as f64).sqrt()\n    }\n}\n\nfn main() {\n    let p = Point { x: 3, y: 4 };\n    let r = &p; // &Point\n    println!("{}", r.distance_from_origin()); // 5.0\n}
\n\n

Aquí r es &Point. El método distance_from_origin toma &self. El compilador ve: r es &Point. Intenta encontrar el método para &Point — ¡lo encuentra! Todo funciona sin desreferencia explícita.

\n\n

Ejemplo 2: Referencia doble

\n\n
fn main() {\n    let p = Point { x: 3, y: 4 };\n    let r1 = &p;     // &Point\n    let r2 = &r1;    // &&Point\n    println!("{}", r2.distance_from_origin()); // 5.0\n}
\n\n

El tipo de r2 es &&Point. El compilador intenta encontrar el método para &&Point — no. Intenta tomar referencia: &&&Point — tampoco. Luego aplica Deref una vez: obtiene &Point. Para

Blogs

Book Recommendations