ProgramaciónDesarrollador Backend en Rust

¿Cómo funcionan los métodos y las funciones asociadas en Rust? ¿En qué se diferencian entre sí, cómo se declaran y se llaman, y cuándo se debe usar qué variante?

Supere entrevistas con el asistente de IA Hintsage

Respuesta.

Historia de la pregunta

Rust toma prestada la concepción de métodos de los lenguajes orientados a objetos, pero los implementa de manera diferente: en lugar del habitual this o self, los métodos aceptan explícitamente un parámetro self. Las funciones asociadas surgieron como una alternativa a los métodos estáticos en otros lenguajes de programación: están asociadas a un tipo, pero no a un valor concreto.

Problema

A menudo se confunden los métodos, las funciones asociadas y las funciones libres. No es obvio cuándo usar un método con self y cuándo una función asociada sin self. Existen preguntas sobre visibilidad, autodereferenciación y transferencia de propiedad.

Solución

Los métodos en Rust se declaran con el primer parámetro self/ &self/ &mut self dentro de un bloque impl (normalmente para struct o enum). Se llaman en una instancia: object.method(). Las funciones asociadas (por ejemplo, new, from) se declaran también dentro de impl, pero sin el primer parámetro self y se llaman a través de dos puntos: Type::function().

Ejemplo de código:

struct Point { x: f64, y: f64, } impl Point { // Función asociada (constructor) fn new(x: f64, y: f64) -> Self { Self { x, y } } // Método: requiere self fn distance_from_origin(&self) -> f64 { (self.x.powi(2) + self.y.powi(2)).sqrt() } } let p = Point::new(3.0, 4.0); printf!("{}", p.distance_from_origin()); // 5.0

Características clave:

  • Los métodos aceptan el parámetro self en diferentes variantes de propiedad.
  • Las funciones asociadas no aceptan self, se usan comúnmente para inicialización o utilidades.
  • Solo los métodos pueden ser llamados en una instancia a través del punto, las funciones asociadas — solo a través de ::

Preguntas con trampa.

¿Se pueden llamar funciones asociadas a través de una instancia (a través del punto)?

Esto es posible (por ejemplo, p.new(1.0, 2.0)), pero se desaconseja encarecidamente: esto puede causar confusión, ya que la función asociada no tiene acceso al objeto actual, y la instancia se pasa sin consideración. Es mejor usar la sintaxis Type::func().

Ejemplo de código:

let p = Point::new(1.0, 2.0); let q = p.new(0.0, 0.0); // Funciona, pero no es la mejor práctica!

¿Pueden los métodos ser asincrónicos?

Sí. Los métodos pueden declararse con la palabra clave async de la misma manera que las funciones libres:

impl Foo { async fn do_async(&self) { // ... } }

¿Se pueden declarar tanto métodos como funciones asociadas dentro de un mismo bloque impl?

Sí, cualquier combinación es válida. También es posible declarar varios bloques impl para un mismo tipo.

Errores comunes y anti-patrón

  • Confundir métodos y funciones asociadas.
  • Dejar funciones asociadas sin especificar pertenencia a un tipo (no declarar en impl).
  • Llamar a funciones asociadas a través de una instancia (a través del punto).

Ejemplo de la vida real

Caso negativo

Un novato declaró la función new fuera de impl y trató de usarla como constructor, y luego accidentalmente llamó a través de una instancia: p.new(1.0, 2.0).

Ventajas:

Funciona rápidamente (el compilador lo permite).

Desventajas:

El código es ilegible, difícil de mantener, complicado de usar los métodos con la propiedad self correcta.

Caso positivo

Todos los métodos y funciones asociadas se declaran estrictamente dentro de impl, se utilizan las sintaxis de llamada correctas (Type::new() para constructores, obj.method() para acciones).

Ventajas:

Alta legibilidad, conformidad con las mejores prácticas.

Desventajas:

Requiere conocimiento de las idiomaticas de Rust y atención a la sintaxis.