Saltearse al contenido

Move 2.0

Desde la versión del compilador 2.0

Move 2.0 introduce varias características nuevas significativas al lenguaje Move, incluyendo mejoras de sintaxis, nuevos tipos de datos y herramientas de desarrollo mejoradas. Esta página resume las adiciones y cambios principales introducidos en Move 2.0.

Los tipos enum permiten definir tipos con múltiples variantes, habilitando patrones de programación más expresivos:

enum Shape {
Circle { radius: u64 },
Rectangle { width: u64, height: u64 },
Triangle { base: u64, height: u64 }
}
enum Option<T> {
Some(T),
None
}
enum Result<T, E> {
Ok(T),
Err(E)
}

Beneficios:

  • Modelado de datos más expresivo
  • Coincidencia de patrones exhaustiva
  • Mejor manejo de errores
  • Tipos de datos algebraicos

Ver: Guía detallada de Enums

2. Coincidencia de Patrones (Pattern Matching)

Sección titulada «2. Coincidencia de Patrones (Pattern Matching)»

La expresión match proporciona una manera poderosa de trabajar con enums y otros tipos:

fun calculate_area(shape: Shape): u64 {
match (shape) {
Shape::Circle { radius } => 3 * radius * radius, // π ≈ 3
Shape::Rectangle { width, height } => width * height,
Shape::Triangle { base, height } => base * height / 2,
}
}
fun handle_result<T, E>(result: Result<T, E>): T {
match (result) {
Result::Ok(value) => value,
Result::Err(_error) => abort 1,
}
}

Características:

  • Coincidencia exhaustiva (todos los casos deben manejarse)
  • Deconstrucción de patrones
  • Guardas de patrones para lógica condicional
  • Patrones comodín (_) para valores ignorados

Nueva sintaxis más ergonómica para iterar sobre colecciones:

fun sum_vector(numbers: &vector<u64>): u64 {
let mut sum = 0;
for (num in numbers) {
sum = sum + *num;
};
sum
}
fun process_range() {
for (i in 0..10) {
// Procesar índice i (0 a 9)
debug::print(&i);
};
}

Mejoras:

  • Sintaxis más limpia e intuitiva
  • Soporte para iteradores
  • Rangos incorporados
  • Mejor ergonomía de desarrollo

Declaración explícita de mutabilidad para mayor claridad:

fun mutable_variables() {
let x = 10; // Inmutable por defecto
let mut y = 20; // Explícitamente mutable
// x = 15; // Error: x es inmutable
y = 25; // OK: y es mutable
let mut vec = vector::empty<u64>();
vector::push_back(&mut vec, 1);
vector::push_back(&mut vec, 2);
}

Ventajas:

  • Intención clara del código
  • Prevención de mutaciones accidentales
  • Mejor documentación de la lógica
  • Herramientas de linting mejoradas

Sintaxis directa para acceso a elementos de vectores:

fun vector_indexing() {
let vec = vector[1, 2, 3, 4, 5];
// Nuevo: acceso directo por índice
let first = vec[0]; // 1
let third = vec[2]; // 3
// Comparar con la sintaxis anterior:
// let first = *vector::borrow(&vec, 0);
// let third = *vector::borrow(&vec, 2);
}
fun mutable_indexing() {
let mut vec = vector[10, 20, 30];
// Modificación directa
vec[0] = 100; // vec ahora es [100, 20, 30]
vec[2] = 300; // vec ahora es [100, 20, 300]
}

Sintaxis más concisa para crear vectores:

fun vector_literals() {
// Nueva sintaxis
let numbers = vector[1, 2, 3, 4, 5];
let strings = vector[b"hello", b"world"];
let addresses = vector[@0x1, @0x2, @0x3];
// Vector vacío con tipo inferido
let empty: vector<u64> = vector[];
// Vector con repetición
let zeros = vector[0; 10]; // [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
}

Manejo simplificado de errores para tipos Result:

fun divide(x: u64, y: u64): Result<u64, String> {
if (y == 0) {
Result::Err(string::utf8(b"División por cero"))
} else {
Result::Ok(x / y)
}
}
fun complex_calculation(a: u64, b: u64, c: u64): Result<u64, String> {
let step1 = divide(a, b)?; // Propaga error si divide falla
let step2 = divide(step1, c)?; // Propaga error si divide falla
Result::Ok(step2 * 2)
}
// Sin el operador ?, sería:
fun complex_calculation_verbose(a: u64, b: u64, c: u64): Result<u64, String> {
match (divide(a, b)) {
Result::Ok(step1) => match (divide(step1, c)) {
Result::Ok(step2) => Result::Ok(step2 * 2),
Result::Err(error) => Result::Err(error),
},
Result::Err(error) => Result::Err(error),
}
}

Mejor soporte para puntos de entrada de scripts:

script {
use std::debug;
fun main() {
debug::print(&b"¡Hola, Move 2.0!");
let shapes = vector[
Shape::Circle { radius: 5 },
Shape::Rectangle { width: 4, height: 6 },
];
for (shape in &shapes) {
let area = calculate_area(*shape);
debug::print(&area);
};
}
}

Move 2.0 proporciona mensajes de error más claros y útiles:

// Error anterior:
// Error: Invalid argument
// Error en Move 2.0:
// Error en la línea 15: El patrón match no es exhaustivo
// Falta el caso: Shape::Triangle { .. }
// Sugerencia: Agrega el patrón faltante o usa un comodín '_'

Formateo automático de código para consistencia:

Ventana de terminal
# Formatear un archivo
aptos move fmt src/my_module.move
# Formatear todo el proyecto
aptos move fmt

Detección de problemas comunes y sugerencias de mejores prácticas:

Ventana de terminal
# Ejecutar linter
aptos move lint
# Ejemplo de sugerencias:
# Warning: Variable 'unused_var' nunca se usa
# Suggestion: Considera usar '_unused_var' o eliminar la variable
# Warning: El match podría simplificarse usando un patrón comodín

REPL para experimentación rápida:

Ventana de terminal
# Iniciar modo interactivo
aptos move shell
# Ejemplo de sesión:
> let x = 42;
> let y = x * 2;
> debug::print(&y);
84
module 0x42::example_old {
use std::vector;
use std::option::{Self, Option};
public fun process_data(data: vector<u64>): Option<u64> {
if (vector::is_empty(&data)) {
option::none()
} else {
let sum = 0;
let len = vector::length(&data);
let i = 0;
while (i < len) {
sum = sum + *vector::borrow(&data, i);
i = i + 1;
};
option::some(sum / len)
}
}
}
module 0x42::example_new {
use std::vector;
enum Option<T> {
Some(T),
None
}
public fun process_data(data: vector<u64>): Option<u64> {
if (vector::is_empty(&data)) {
Option::None
} else {
let mut sum = 0;
for (value in &data) {
sum = sum + *value;
};
Option::Some(sum / vector::length(&data))
}
}
public fun use_result(data: vector<u64>): u64 {
match (process_data(data)) {
Option::Some(average) => average,
Option::None => 0,
}
}
}
module 0x42::error_handling {
enum Error {
DivisionByZero,
InvalidInput,
Overflow
}
enum Result<T, E> {
Ok(T),
Err(E)
}
public fun safe_divide(x: u64, y: u64): Result<u64, Error> {
if (y == 0) {
Result::Err(Error::DivisionByZero)
} else {
Result::Ok(x / y)
}
}
public fun chain_operations(a: u64, b: u64, c: u64): Result<u64, Error> {
let step1 = safe_divide(a, b)?;
let step2 = safe_divide(step1, c)?;
Result::Ok(step2)
}
public fun handle_errors() {
let result = chain_operations(100, 5, 2);
match (result) {
Result::Ok(value) => {
debug::print(&string::utf8(b"Resultado: "));
debug::print(&value);
},
Result::Err(Error::DivisionByZero) => {
debug::print(&string::utf8(b"Error: División por cero"));
},
Result::Err(_) => {
debug::print(&string::utf8(b"Error desconocido"));
}
}
}
}

Move 2.0 mantiene compatibilidad hacia atrás con la mayoría del código Move 1.x:

  • ✅ Los módulos existentes compilan sin cambios
  • ✅ Las funciones públicas mantienen sus firmas
  • ✅ Los tipos struct existentes funcionan igual
  • ✅ Los patrones de almacenamiento global son idénticos

Las nuevas características son opt-in y no rompen el código existente:

// Este código Move 1.x sigue funcionando en Move 2.0
module 0x42::legacy {
use std::vector;
struct OldStruct {
data: u64
}
public fun old_function(x: u64): u64 {
x + 1
}
}

Puedes adoptar características de Move 2.0 gradualmente:

module 0x42::gradual_migration {
// Usar nuevas características donde sea beneficioso
enum Status {
Active,
Inactive,
Pending
}
// Mantener código existente donde funcione bien
struct LegacyData {
value: u64,
owner: address,
}
// Mezclar sintaxis nueva y antigua
public fun process(data: vector<u64>): u64 {
let mut sum = 0; // Nueva sintaxis mutable
// Bucle tradicional sigue funcionando
let len = vector::length(&data);
let i = 0;
while (i < len) {
sum = sum + *vector::borrow(&data, i);
i = i + 1;
};
sum
}
}
Ventana de terminal
# Nuevo comando para proyectos Move 2.0
aptos move init --template move2.0
# Compilar con características Move 2.0
aptos move compile --language-version 2.0
# Testing con nuevas características
aptos move test --language-version 2.0
# Formateo de código
aptos move fmt
# Linting avanzado
aptos move lint --level strict

Las extensiones de VSCode y otros IDEs ahora soportan:

  • ✅ Resaltado de sintaxis para enums y match
  • ✅ Autocompletado para nuevas características
  • ✅ Detección de patrones match no exhaustivos
  • ✅ Refactoring automático para bucles for
  • ✅ Hover information para tipos enum
// Nuevas macros de debugging
fun debug_example() {
let data = vector[1, 2, 3];
// Imprimir con formato mejorado
debug::print_stack_trace();
debug::print_struct(&data);
// Assertions más informativos
debug::assert!(vector::length(&data) == 3, b"Vector debe tener 3 elementos");
}
// ✅ Bueno - estados explícitos con enums
enum OrderStatus {
Pending { created_at: u64 },
Processing { started_at: u64 },
Shipped { tracking_number: vector<u8> },
Delivered { delivered_at: u64 },
Cancelled { reason: vector<u8> }
}
// ❌ Evitar - estados implícitos con números
struct Order {
status: u8, // 0=pending, 1=processing, etc.
}
// ✅ Bueno - errores explícitos
enum TransferError {
InsufficientBalance,
InvalidRecipient,
AmountTooLarge
}
public fun transfer(from: address, to: address, amount: u64): Result<(), TransferError> {
// Implementación con manejo explicito de errores
}
// ❌ Evitar - abort codes sin contexto
public fun transfer_old(from: address, to: address, amount: u64) {
assert!(balance >= amount, 1); // ¿Qué significa 1?
}
// ✅ Bueno - todos los casos manejados
fun handle_status(status: OrderStatus): vector<u8> {
match (status) {
OrderStatus::Pending{..} => b"Orden pendiente",
OrderStatus::Processing{..} => b"Procesando orden",
OrderStatus::Shipped{..} => b"Orden enviada",
OrderStatus::Delivered{..} => b"Orden entregada",
OrderStatus::Cancelled{..} => b"Orden cancelada",
}
}
// ❌ Evitar - casos faltantes
fun handle_status_incomplete(status: OrderStatus): vector<u8> {
match (status) {
OrderStatus::Pending{..} => b"Orden pendiente",
OrderStatus::Processing{..} => b"Procesando orden",
// Faltan casos - error de compilación
}
}
// ✅ Bueno - mutabilidad explícita y mínima
fun calculate_sum(numbers: &vector<u64>): u64 {
let mut sum = 0; // Solo esta variable es mutable
let multiplier = 2; // Inmutable
for (num in numbers) {
sum = sum + (*num * multiplier);
};
sum
}

Move 2.0 es la base para futuras mejoras:

  • Generics mejorados con constraints más expresivos
  • Traits/Interfaces para polimorfismo
  • Async/await para operaciones asíncronas
  • Macros para metaprogramación
  • FFI mejorada para interoperabilidad
  • Debugger visual con stepping through code
  • Performance profiler para optimización
  • Documentation generator automático
  • Package manager mejorado
  • Testing framework avanzado

Move 2.0 representa una evolución significativa del lenguaje Move, introduciendo características que hacen el desarrollo más expresivo, seguro y ergonómico. Las principales mejoras incluyen:

Enums y pattern matching para modelado de datos rico
Sintaxis mejorada para mejor ergonomía de desarrollador
Manejo de errores robusto con tipos Result
Herramientas de desarrollo avanzadas
Compatibilidad hacia atrás para migración suave

Con estas características, Move 2.0 habilita patrones de programación más sofisticados mientras mantiene las garantías de seguridad y simplicidad que hacen a Move único en el espacio de smart contracts.

Para desarrolladores nuevos en Move, se recomienda comenzar directamente con Move 2.0. Para desarrolladores existentes, la migración puede ser gradual, adoptando nuevas características donde aporten valor mientras mantienen el código existente funcionando.