Igualdad
Move soporta dos operaciones de igualdad == e != (igual y no igual). Estas operaciones están disponibles solo para ciertos tipos, y ambos operandos deben ser del mismo tipo (no hay coerción implícita).
Las operaciones de igualdad retornan un valor bool y son las siguientes:
| Sintaxis | Operación | Descripción | 
|---|---|---|
| == | igual | Retorna truesi los dos operandos son iguales | 
| != | no igual | Retorna truesi los dos operandos no son iguales | 
Tipos Soportados
Sección titulada «Tipos Soportados»Ambos operadores de igualdad pueden usarse con todos los tipos primitivos: u8, u16, u32, u64, u128, u256, bool, y address. También están soportados para vector, pero el tipo del vector debe a su vez soportar igualdad.
script {  fun example() {    // Tipos primitivos    assert!(0u8 == 0u8, 0);    assert!(1u64 != 2u64, 1);    assert!(true == true, 2);    assert!(false != true, 3);    assert!(@0x1 == @0x1, 4);    assert!(@0x1 != @0x2, 5);
    // Vectores    assert!(vector<u8>[1, 2, 3] == vector<u8>[1, 2, 3], 6);    assert!(vector<u8>[1, 2, 3] != vector<u8>[3, 2, 1], 7);    assert!(vector<bool>[true, false] == vector<bool>[true, false], 8);  }}Tipos No Soportados
Sección titulada «Tipos No Soportados»Los operadores de igualdad no están soportados para:
- Structs (incluidos recursos)
- Referencias
- Tuplas
- Funciones
script {  struct S { f: u64 }
  fun example() {    let s1 = S { f: 0 };    let s2 = S { f: 0 };
    // Error: igualdad no soportada para structs    // let are_equal = s1 == s2;
    // Error: igualdad no soportada para referencias    // let ref1 = &s1;    // let ref2 = &s2;    // let refs_equal = ref1 == ref2;  }}Semántica de Igualdad
Sección titulada «Semántica de Igualdad»Comparación por Valor
Sección titulada «Comparación por Valor»Para tipos primitivos, la igualdad compara los valores directamente:
script {  fun value_comparison() {    let a = 42u64;    let b = 42u64;    let c = 43u64;
    assert!(a == b, 0); // Mismo valor    assert!(a != c, 1); // Valores diferentes
    // Para direcciones    let addr1 = @0x42;    let addr2 = @0x42;    let addr3 = @0x43;
    assert!(addr1 == addr2, 2); // Misma dirección    assert!(addr1 != addr3, 3); // Direcciones diferentes  }}Comparación de Vectores
Sección titulada «Comparación de Vectores»Para vectores, la igualdad compara elemento por elemento:
script {  fun vector_comparison() {    let vec1 = vector<u64>[1, 2, 3];    let vec2 = vector<u64>[1, 2, 3];    let vec3 = vector<u64>[1, 2, 4];    let vec4 = vector<u64>[1, 2];
    assert!(vec1 == vec2, 0); // Mismos elementos en mismo orden    assert!(vec1 != vec3, 1); // Diferentes elementos    assert!(vec1 != vec4, 2); // Diferentes longitudes
    // Vector vacío    let empty1 = vector<u64>[];    let empty2 = vector<u64>[];    assert!(empty1 == empty2, 3); // Vectores vacíos son iguales  }}Comparación de Vectores Anidados
Sección titulada «Comparación de Vectores Anidados»script {  fun nested_vector_comparison() {    let nested1 = vector<vector<u8>>[      vector<u8>[1, 2],      vector<u8>[3, 4]    ];
    let nested2 = vector<vector<u8>>[      vector<u8>[1, 2],      vector<u8>[3, 4]    ];
    let nested3 = vector<vector<u8>>[      vector<u8>[1, 2],      vector<u8>[3, 5] // Diferente elemento    ];
    assert!(nested1 == nested2, 0); // Misma estructura    assert!(nested1 != nested3, 1); // Elementos diferentes  }}Casos de Uso Comunes
Sección titulada «Casos de Uso Comunes»Validación de Entrada
Sección titulada «Validación de Entrada»module 0x42::validation {  use std::string::{Self, String};  use std::vector;
  const E_INVALID_INPUT: u64 = 1;  const E_EMPTY_STRING: u64 = 2;
  public fun validate_non_zero(value: u64) {    assert!(value != 0, E_INVALID_INPUT);  }
  public fun validate_address(addr: address) {    assert!(addr != @0x0, E_INVALID_INPUT);  }
  public fun validate_non_empty_string(s: &String) {    let empty = string::utf8(b"");    assert!(*s != empty, E_EMPTY_STRING);  }
  public fun validate_expected_value(actual: u64, expected: u64) {    assert!(actual == expected, E_INVALID_INPUT);  }}Búsqueda en Vectores
Sección titulada «Búsqueda en Vectores»module 0x42::search {  use std::vector;  use std::option::{Self, Option};
  public fun find_index<T>(vec: &vector<T>, target: &T): Option<u64> {    let len = vector::length(vec);    let mut i = 0;
    while (i < len) {      if (vector::borrow(vec, i) == target) {        return option::some(i)      };      i = i + 1;    };
    option::none()  }
  public fun contains<T>(vec: &vector<T>, target: &T): bool {    option::is_some(&find_index(vec, target))  }
  public fun count_occurrences<T>(vec: &vector<T>, target: &T): u64 {    let len = vector::length(vec);    let mut count = 0;    let mut i = 0;
    while (i < len) {      if (vector::borrow(vec, i) == target) {        count = count + 1;      };      i = i + 1;    };
    count  }}Filtrado de Datos
Sección titulada «Filtrado de Datos»module 0x42::filtering {  use std::vector;
  public fun filter_equal<T: copy>(vec: &vector<T>, target: &T): vector<T> {    let result = vector::empty<T>();    let len = vector::length(vec);    let mut i = 0;
    while (i < len) {      let element = vector::borrow(vec, i);      if (element == target) {        vector::push_back(&mut result, *element);      };      i = i + 1;    };
    result  }
  public fun filter_not_equal<T: copy>(vec: &vector<T>, target: &T): vector<T> {    let result = vector::empty<T>();    let len = vector::length(vec);    let mut i = 0;
    while (i < len) {      let element = vector::borrow(vec, i);      if (element != target) {        vector::push_back(&mut result, *element);      };      i = i + 1;    };
    result  }
  public fun remove_duplicates<T: copy>(vec: &vector<T>): vector<T> {    let result = vector::empty<T>();    let len = vector::length(vec);    let mut i = 0;
    while (i < len) {      let element = vector::borrow(vec, i);      if (!contains(&result, element)) {        vector::push_back(&mut result, *element);      };      i = i + 1;    };
    result  }
  fun contains<T>(vec: &vector<T>, target: &T): bool {    let len = vector::length(vec);    let mut i = 0;
    while (i < len) {      if (vector::borrow(vec, i) == target) {        return true      };      i = i + 1;    };
    false  }}Comparación de Estados
Sección titulada «Comparación de Estados»module 0x42::state_machine {  const PENDING: u8 = 0;  const PROCESSING: u8 = 1;  const COMPLETED: u8 = 2;  const FAILED: u8 = 3;
  struct Order has key {    id: u64,    status: u8,    owner: address,  }
  public fun is_pending(order: &Order): bool {    order.status == PENDING  }
  public fun is_processing(order: &Order): bool {    order.status == PROCESSING  }
  public fun is_completed(order: &Order): bool {    order.status == COMPLETED  }
  public fun is_failed(order: &Order): bool {    order.status == FAILED  }
  public fun can_process(order: &Order): bool {    order.status == PENDING  }
  public fun can_complete(order: &Order): bool {    order.status == PROCESSING  }
  public fun is_owned_by(order: &Order, addr: address): bool {    order.owner == addr  }}Optimizaciones y Rendimiento
Sección titulada «Optimizaciones y Rendimiento»Comparación Temprana
Sección titulada «Comparación Temprana»Para vectores largos, considera salir temprano si es posible:
module 0x42::optimized_comparison {  use std::vector;
  public fun vectors_equal_optimized<T>(vec1: &vector<T>, vec2: &vector<T>): bool {    let len1 = vector::length(vec1);    let len2 = vector::length(vec2);
    // Salida temprana si las longitudes son diferentes    if (len1 != len2) {      return false    };
    // Si ambos están vacíos, son iguales    if (len1 == 0) {      return true    };
    // Comparar elemento por elemento    let mut i = 0;    while (i < len1) {      if (vector::borrow(vec1, i) != vector::borrow(vec2, i)) {        return false      };      i = i + 1;    };
    true  }
  public fun find_first_difference<T>(vec1: &vector<T>, vec2: &vector<T>): Option<u64> {    let len1 = vector::length(vec1);    let len2 = vector::length(vec2);    let min_len = if (len1 < len2) len1 else len2;
    let mut i = 0;    while (i < min_len) {      if (vector::borrow(vec1, i) != vector::borrow(vec2, i)) {        return option::some(i)      };      i = i + 1;    };
    // Si todas las posiciones comparables son iguales pero las longitudes difieren    if (len1 != len2) {      option::some(min_len)    } else {      option::none()    }  }}Evitar Comparaciones Innecesarias
Sección titulada «Evitar Comparaciones Innecesarias»module 0x42::efficient_checks {  use std::vector;
  public fun has_target_at_positions<T>(    vec: &vector<T>,    target: &T,    positions: &vector<u64>  ): bool {    let pos_len = vector::length(positions);    let vec_len = vector::length(vec);    let mut i = 0;
    while (i < pos_len) {      let pos = *vector::borrow(positions, i);
      // Verificar límites antes de acceder      if (pos >= vec_len) {        return false      };
      // Verificar igualdad      if (vector::borrow(vec, pos) != target) {        return false      };
      i = i + 1;    };
    true  }}Patrones de Comparación para Structs
Sección titulada «Patrones de Comparación para Structs»Aunque los structs no soportan igualdad directa, puedes implementar funciones de comparación personalizadas:
module 0x42::custom_equality {  struct Point {    x: u64,    y: u64,  }
  struct Person {    name: vector<u8>,    age: u8,    address: address,  }
  public fun points_equal(p1: &Point, p2: &Point): bool {    p1.x == p2.x && p1.y == p2.y  }
  public fun persons_equal(p1: &Person, p2: &Person): bool {    p1.name == p2.name && p1.age == p2.age && p1.address == p2.address  }
  // Comparación parcial - solo por nombre  public fun same_name(p1: &Person, p2: &Person): bool {    p1.name == p2.name  }
  // Comparación con tolerancia para puntos  public fun points_near(p1: &Point, p2: &Point, tolerance: u64): bool {    let x_diff = if (p1.x > p2.x) p1.x - p2.x else p2.x - p1.x;    let y_diff = if (p1.y > p2.y) p1.y - p2.y else p2.y - p1.y;
    x_diff <= tolerance && y_diff <= tolerance  }}Casos Especiales y Gotchas
Sección titulada «Casos Especiales y Gotchas»Precisión Flotante (No Aplicable a Move)
Sección titulada «Precisión Flotante (No Aplicable a Move)»A diferencia de lenguajes con números de punto flotante, Move solo tiene enteros, por lo que no hay problemas de precisión flotante:
script {  fun integer_equality() {    // En Move, esto siempre es seguro    let a = 1;    let b = 1;    assert!(a == b, 0); // Siempre funciona como se espera
    // No hay problemas de precisión flotante como 0.1 + 0.2 != 0.3  }}Vectores Vacíos
Sección titulada «Vectores Vacíos»script {  fun empty_vector_equality() {    let empty1 = vector<u64>[];    let empty2 = vector<bool>[];
    // Vectores vacíos de diferentes tipos NO son iguales    // empty1 == empty2; // Error de tipo
    let empty3 = vector<u64>[];    assert!(empty1 == empty3, 0); // OK - mismo tipo  }}Vectores de Bytes
Sección titulada «Vectores de Bytes»script {  fun byte_vector_equality() {    let bytes1 = b"hello";    let bytes2 = vector<u8>[104, 101, 108, 108, 111]; // "hello" en ASCII
    assert!(bytes1 == bytes2, 0); // Son iguales
    let bytes3 = b"Hello"; // Diferente capitalización    assert!(bytes1 != bytes3, 1); // No son iguales  }}Mejores Prácticas
Sección titulada «Mejores Prácticas»1. Usa Nombres Descriptivos para Comparaciones
Sección titulada «1. Usa Nombres Descriptivos para Comparaciones»// ✅ Bueno - intención clarapublic fun is_admin(user_role: u8): bool {  user_role == ADMIN_ROLE}
public fun is_zero_address(addr: address): bool {  addr == @0x0}
// ❌ Malo - intención poco clarapublic fun check(x: u8): bool {  x == 1}2. Combina Comparaciones Lógicamente
Sección titulada «2. Combina Comparaciones Lógicamente»// ✅ Bueno - lógica clarapublic fun is_valid_user(age: u8, address: address): bool {  age >= 18 && address != @0x0}
public fun is_special_case(status: u8, priority: u8): bool {  status == URGENT_STATUS || priority == HIGH_PRIORITY}
// ❌ Malo - comparaciones complejas anidadaspublic fun complex_check(a: u8, b: u8, c: u8): bool {  if (a == 1) {    if (b == 2) {      c == 3    } else {      false    }  } else {    false  }}3. Implementa Funciones de Igualdad para Structs
Sección titulada «3. Implementa Funciones de Igualdad para Structs»module 0x42::best_practices {  struct User {    id: u64,    name: vector<u8>,    active: bool,  }
  // ✅ Bueno - función de igualdad clara  public fun users_equal(u1: &User, u2: &User): bool {    u1.id == u2.id &&    u1.name == u2.name &&    u1.active == u2.active  }
  // ✅ También bueno - comparaciones específicas  public fun same_user_id(u1: &User, u2: &User): bool {    u1.id == u2.id  }
  public fun same_user_name(u1: &User, u2: &User): bool {    u1.name == u2.name  }}4. Maneja Casos Edge
Sección titulada «4. Maneja Casos Edge»module 0x42::edge_cases {  use std::vector;  use std::option::{Self, Option};
  public fun safe_vector_equal<T>(    vec1: &Option<vector<T>>,    vec2: &Option<vector<T>>  ): bool {    if (option::is_none(vec1) && option::is_none(vec2)) {      true    } else if (option::is_some(vec1) && option::is_some(vec2)) {      *option::borrow(vec1) == *option::borrow(vec2)    } else {      false    }  }
  public fun compare_with_default<T: copy>(    actual: &Option<T>,    expected: T  ): bool {    if (option::is_some(actual)) {      *option::borrow(actual) == expected    } else {      false    }  }}Errores Comunes
Sección titulada «Errores Comunes»Intentar Comparar Tipos Incompatibles
Sección titulada «Intentar Comparar Tipos Incompatibles»// ❌ Error - tipos diferentesscript {  fun type_mismatch() {    let a = 1u64;    let b = 1u8;    // let result = a == b; // Error: tipos incompatibles  }}
// ✅ Solución - conversión explícitascript {  fun correct_comparison() {    let a = 1u64;    let b = 1u8;    let result = a == (b as u64); // OK  }}Intentar Comparar Structs Directamente
Sección titulada «Intentar Comparar Structs Directamente»// ❌ Error - structs no soportan igualdadmodule 0x42::struct_error {  struct Point { x: u64, y: u64 }
  fun compare_structs() {    let p1 = Point { x: 1, y: 2 };    let p2 = Point { x: 1, y: 2 };    // let equal = p1 == p2; // Error  }}
// ✅ Solución - función de comparación personalizadamodule 0x42::struct_solution {  struct Point { x: u64, y: u64 }
  public fun points_equal(p1: &Point, p2: &Point): bool {    p1.x == p2.x && p1.y == p2.y  }
  fun compare_structs() {    let p1 = Point { x: 1, y: 2 };    let p2 = Point { x: 1, y: 2 };    let equal = points_equal(&p1, &p2); // OK  }}Conclusión
Sección titulada «Conclusión»La igualdad en Move es un concepto fundamental pero limitado en comparación con otros lenguajes. Entender qué tipos soportan igualdad y cómo implementar comparaciones personalizadas para tipos que no la soportan es crucial para escribir código Move efectivo.
Puntos clave para recordar:
- Solo tipos primitivos y vectores soportan operadores de igualdad
- Los structs requieren funciones de comparación personalizadas
- Las comparaciones de vectores son elemento por elemento
- Siempre considera casos edge como vectores vacíos
- Usa nombres descriptivos para funciones de comparación
- La igualdad en Move es siempre por valor, no por referencia