En Rust, la sobrecarga de operadores permite definir el comportamiento de operadores aritméticos, lógicos y de comparación para tipos de datos personalizados. A diferencia de otros lenguajes que permiten la sobrecarga de cualquier función, Rust utiliza un sistema basado en traits definidos en el módulo std::ops. Esto garantiza que el comportamiento de los operadores sea predecible y siga las reglas de tipado del compilador.

Para implementar la sobrecarga, se deben implementar traits específicos como Add para el operador +, Sub para -, Mul para *, Div para /, y Neg para la negación unaria -x. También existen traits para el acceso a índices (Index e IndexMut) y para la dereferencia (Deref).

El uso de estos traits es ideal cuando se crean tipos que representan entidades matemáticas (como vectores, matrices o números complejos) o estructuras de datos que requieren una sintaxis natural (como contenedores). Sin embargo, es vital mantener la coherencia semántica: sobrecargar un operador para realizar una acción que no tenga relación intuitiva con el símbolo (por ejemplo, usar + para borrar un elemento de una lista) confunde al desarrollador y rompe el principio de menor sorpresa.

use std::ops::{Add, Sub, Neg};

// Definición de una estructura para representar un vector en 2D
#[derive(Debug, PartialEq)]
struct Vector2D {
    x: f64,
    y: f64,
}

// Implementación de la suma: Vector2D + Vector2D = Vector2D
impl Add for Vector2D {
    type Output = Self;

    fn add(self, rhs: Self) -> Self {
        Self {
            x: self.x + rhs.x,
            y: self.y + rhs.y,
        }
    }
}

// Implementación de la resta: Vector2D - Vector2D = Vector2D
impl Sub for Vector2D {
    type Output = Self;

    fn sub(self, rhs: Self) -> Self {
        Self {
            x: self.x - rhs.x,
            y: self.y - rhs.y,
        }
    }
}

// Implementación de la negación unaria: -Vector2D = Vector2D (con signos invertidos)
impl Neg for Vector2D {
    type Output = Self;

    fn neg(self) -> Self {
        Self {
            x: -self.x,
            y: -self.y,
        }
    }
}

fn main() {
    let v1 = Vector2D { x: 10.0, y: 20.0 };
    let v2 = Vector2D { x: 5.0, y: 5.0 };

    // Uso de la sobrecarga de suma (+)
    let suma = v1 + v2; 
    
    // Uso de la sobrecarga de resta (-)
    let resta = v1 - v2;

    // Uso de la sobrecarga de negación unaria (-)
    let negado = -v1;

    println!("Vector 1: {:?}", v1);
    println!("Vector 2: {:?}", v2);
    println!("Suma: {:?}", suma);
    println!("Resta: {:?}", resta);
    println!("Negado de v1: {:?}", negado);
}

Explicación del Código


use std::ops::{Add, Sub, Neg};: Importamos los traits necesarios para sobrecargar la suma, la resta y la negación.
struct Vector2D { x: f64, y: f64 }: Definimos nuestra estructura con dos campos de tipo f64. Usamos #[derive(Debug, PartialEq)] para poder imprimir la estructura y compararla.
impl Add for Vector2D: Implementamos el trait Add. El tipo asociado type Output = Self; especifica que el resultado de la operación será otro Vector2D.
fn add(self, rhs: Self) -> Self: El método add recibe self (el operando de la izquierda) y rhs (Right Hand Side, el operando de la derecha). Sumamos sus componentes x e y correspondientes.
impl Sub for Vector2D: Implementamos Sub para permitir la operación v1 - v2. Al igual que en Add, el operando derecho se denomina rhs.
impl Neg for Vector2D: Implementamos Neg para el operador unario. A diferencia de los anteriores, neg no recibe un segundo operando, solo self.
– En main, la expresión v1 + v2 es una llamada al método add de v1 pasando a v2 como argumento, lo que permite una sintaxis limpia y matemática.

119