ProgramaciónDesarrollador de bibliotecas

¿Qué son PartialEq y Eq en Rust, qué contrato garantizan al comparar valores y por qué es importante respetar la estricta igualdad en tipos de usuario?

Supere entrevistas con el asistente de IA Hintsage

Respuesta.

Historia de la pregunta

En Rust, los traits PartialEq y Eq surgen de la filosofía de la seguridad de tipos: todas las operaciones fundamentales para colecciones (HashMap, HashSet) se basan en una definición rigurosa de igualdad. Los traits determinan si los objetos de dos tipos pueden ser comparados usando == y si el tipo se compromete a implementar la "igualdad total" (Eq) o la "igualdad parcial" (PartialEq).

Problema

Una implementación incorrecta de PartialEq/Eq lleva a un mal funcionamiento de las colecciones, donde se violan propiedades matemáticas básicas: la comparación consigo mismo, simetría, transitividad. Además, un Eq incorrecto puede hacer que los objetos "se pierdan" en las colecciones o que aparezcan duplicados inesperados.

Solución

PartialEq se implementa para tipos en los que la comparación no es posible para todos los valores (por ejemplo, f32, donde NaN != NaN). Eq está destinado solo para relaciones "estrictas" (totales). Los tipos de usuario deben implementar la comparación de acuerdo con estos contratos estrictamente.

Ejemplo de código:

#[derive(PartialEq, Eq)] struct Point { x: i32, y: i32, } let p1 = Point { x: 1, y: 2 }; let p2 = Point { x: 1, y: 2 }; assert!(p1 == p2); // true

Características clave:

  • PartialEq permite la comparación "parcial": puede haber valores que no se comparen correctamente (como NaN).
  • Eq exige una relación completa: x == x siempre debe ser true; todas las propiedades de la relación deben cumplirse.
  • Implementar correctamente estos traits es crítico para colecciones y funciones de la biblioteca estándar.

Preguntas capciosas.

¿Por qué para el tipo f32 la biblioteca estándar solo implementa PartialEq y no Eq?

Porque según IEEE-754, NaN != NaN, es decir, la propiedad x == x para todos x no se cumple, y esta es una propiedad necesaria para Eq.

¿Es obligatorio implementar Eq explícitamente si se ha implementado PartialEq manualmente?

Sí, si el tipo soporta igualdad total (x == x siempre true), es necesario implementar Eq manualmente, de lo contrario, algunas estructuras dejarán de compilarse con tu tipo.

¿Puede un usuario implementar PartialEq de manera "asimétrica" (x == y, pero y != x)?

Técnicamente sí, pero esto lleva a un mal funcionamiento de las colecciones y se considera un error.

Errores comunes y anti-patrones

  • Ignorar las propiedades necesarias de comparación, haciendo que la implementación de PartialEq sea "defectuosa".
  • No implementar Eq para aquellos tipos donde esto es posible, rompiendo el funcionamiento de HashMap/HashSet.
  • Implementar la simetría "a su manera", violando la especificación.

Ejemplo de la vida real

** Caso negativo

El usuario implementó PartialEq para un tipo que solo compara un campo, ignorando los demás. Como resultado, HashSet "pierde" duplicados.

Ventajas:

  • Implementación sencilla, se puede comparar rápidamente.

Desventajas:

  • Coincidencias falsas, pérdida de unicidad.

**Caso positivo

Utiliza #[derive(PartialEq, Eq)] o implementa su propia versión de comparación, teniendo en cuenta toda la lógica empresarial, garantizando las propiedades requeridas.

Ventajas:

  • Las colecciones funcionan correctamente, comportamiento estricto.

Desventajas:

  • Requiere un enfoque cuidadoso y reflexivo en el diseño.