Vector
vector<T>
es el único tipo de colección primitiva proporcionado por Move. Un vector<T>
es una colección homogénea de T
s que puede crecer o encoger empujando/extrayendo valores del “final”.
Un vector<T>
puede ser instanciado con cualquier tipo T
. Por ejemplo, vector<u64>
, vector<address>
, vector<0x42::MyModule::MyResource>
, y vector<vector<u8>>
son todos tipos de vector válidos.
Literales
Sección titulada «Literales»Literales vector
Generales
Sección titulada «Literales vector Generales»Los vectores de cualquier tipo pueden ser creados con literales vector
.
Sintaxis | Tipo | Descripción |
---|---|---|
vector[] | vector[]: vector<T> donde T es cualquier tipo único, no-referencia | Un vector vacío |
vector[e1, ..., en] | vector[e1, ..., en]: vector<T> donde e_i: T tal que 0 < i <= n y n > 0 | Un vector con n elementos (de longitud n ) |
En estos casos, el tipo del vector
se infiere, ya sea del tipo de elemento o del uso del vector. Si el tipo no puede ser inferido, o simplemente para mayor claridad, el tipo puede especificarse explícitamente:
vector<T>[]: vector<T>vector<T>[e1, ..., en]: vector<T>
Ejemplos de Literales Vector
Sección titulada «Ejemplos de Literales Vector»script { fun example() { (vector[]: vector<bool>); (vector[0u8, 1u8, 2u8]: vector<u8>); (vector<u128>[]: vector<u128>); (vector<address>[@0x42, @0x100]: vector<address>); }}
Literales vector<u8>
Sección titulada «Literales vector<u8>»Un caso de uso común para vectores en Move es representar “arreglos de bytes”, que se representan con vector<u8>
. Estos valores se usan frecuentemente para propósitos criptográficos, como una clave pública o un resultado de hash. Estos valores son tan comunes que se proporciona sintaxis específica para hacer los valores más legibles, en oposición a tener que usar vector[]
donde cada valor individual u8
se especifica en forma numérica.
Actualmente hay dos tipos soportados de literales vector<u8>
, cadenas de bytes y cadenas hex.
Cadenas de Bytes
Sección titulada «Cadenas de Bytes»Las cadenas de bytes son literales de cadena entre comillas prefijados por una b
, por ejemplo b"Hello!\n"
.
Estas son cadenas codificadas en ASCII que permiten secuencias de escape. Actualmente, las secuencias de escape soportadas son:
Secuencia de Escape | Descripción |
---|---|
\n | Nueva línea (o Line feed) |
\r | Retorno de carro |
\t | Tab |
\\ | Barra invertida |
\0 | Null |
\" | Comilla |
\xHH | Escape hex, inserta la secuencia de bytes hex HH |
Cadenas Hex
Sección titulada «Cadenas Hex»Las cadenas hex son literales de cadena entre comillas prefijados por una x
, por ejemplo x"48656C6C6F210A"
.
Cada par de bytes, de 00
a FF
, se interpreta como valor u8
codificado en hex. Así que cada par de bytes corresponde a una sola entrada en el vector<u8>
resultante.
Ejemplos de Literales de Cadena
Sección titulada «Ejemplos de Literales de Cadena»script { fun byte_and_hex_strings() { assert!(b"" == x"", 0); assert!(b"Hello!\n" == x"48656C6C6F210A", 1); assert!(b"\x48\x65\x6C\x6C\x6F\x21\x0A" == x"48656C6C6F210A", 2); assert!( b"\"Hello\tworld!\"\n \r \\Null=\0" == x"2248656C6C6F09776F726C6421220A200D205C4E756C6C3D00", 3 ); }}
Operaciones
Sección titulada «Operaciones»vector
proporciona varias operaciones vía el módulo std::vector
en la biblioteca estándar de Move, como se muestra abajo. Más operaciones pueden ser agregadas con el tiempo. Documentación actualizada sobre vector
se puede encontrar aquí.
Operaciones Básicas
Sección titulada «Operaciones Básicas»use std::vector;
script { fun example() { // Crear un vector vacío let v = vector::empty<u64>();
// Agregar elementos vector::push_back(&mut v, 1); vector::push_back(&mut v, 2); vector::push_back(&mut v, 3);
// Obtener longitud let len = vector::length(&v); // 3
// Acceder a elementos let first = *vector::borrow(&v, 0); // 1
// Remover elemento del final let last = vector::pop_back(&mut v); // 3
// Verificar si está vacío let is_empty = vector::is_empty(&v); // false }}
Operaciones de Acceso
Sección titulada «Operaciones de Acceso»use std::vector;
public fun access_operations() { let v = vector[10, 20, 30, 40];
// Préstamo inmutable let first_ref = vector::borrow(&v, 0); assert!(*first_ref == 10, 1);
// Préstamo mutable let v_mut = vector[10, 20, 30]; let second_ref = vector::borrow_mut(&mut v_mut, 1); *second_ref = 25;
// Intercambio de elementos vector::swap(&mut v_mut, 0, 2); // [30, 25, 10]
// Reverso vector::reverse(&mut v_mut); // [10, 25, 30]}
Operaciones de Búsqueda
Sección titulada «Operaciones de Búsqueda»use std::vector;
public fun search_operations() { let v = vector[1, 3, 5, 7, 9];
// Contiene elemento let has_five = vector::contains(&v, &5); // true let has_four = vector::contains(&v, &4); // false
// Encontrar índice let (found, index) = vector::index_of(&v, &7); if (found) { // index es 3 };}
Operaciones de Manipulación
Sección titulada «Operaciones de Manipulación»use std::vector;
public fun manipulation_operations() { let mut v1 = vector[1, 2, 3]; let mut v2 = vector[4, 5, 6];
// Concatenar vectores vector::append(&mut v1, v2); // v1 es ahora [1, 2, 3, 4, 5, 6]
// Insertar en posición específica vector::insert(&mut v1, 2, 99); // [1, 2, 99, 3, 4, 5, 6]
// Remover en posición específica let removed = vector::remove(&mut v1, 2); // remueve 99
// Intercambiar y remover let swapped = vector::swap_remove(&mut v1, 0); // remueve 1, eficiente}
Ejemplos Prácticos
Sección titulada «Ejemplos Prácticos»Lista de Tareas
Sección titulada «Lista de Tareas»module 0x42::todo_list { use std::vector; use std::string::String;
struct TodoItem has store, drop { id: u64, description: String, completed: bool }
struct TodoList has key { items: vector<TodoItem>, next_id: u64 }
/// Inicializa una lista de tareas public fun initialize(account: &signer) { move_to(account, TodoList { items: vector::empty<TodoItem>(), next_id: 1 }); }
/// Agrega una nueva tarea public fun add_task(account: &signer, description: String) acquires TodoList { let addr = std::signer::address_of(account); let todo_list = borrow_global_mut<TodoList>(addr);
let new_item = TodoItem { id: todo_list.next_id, description, completed: false };
vector::push_back(&mut todo_list.items, new_item); todo_list.next_id = todo_list.next_id + 1; }
/// Marca una tarea como completada public fun complete_task(account: &signer, task_id: u64) acquires TodoList { let addr = std::signer::address_of(account); let todo_list = borrow_global_mut<TodoList>(addr);
let len = vector::length(&todo_list.items); let mut i = 0; while (i < len) { let item = vector::borrow_mut(&mut todo_list.items, i); if (item.id == task_id) { item.completed = true; break }; i = i + 1; } }
/// Obtiene todas las tareas pendientes public fun get_pending_tasks(addr: address): vector<u64> acquires TodoList { let todo_list = borrow_global<TodoList>(addr); let pending = vector::empty<u64>();
let len = vector::length(&todo_list.items); let mut i = 0; while (i < len) { let item = vector::borrow(&todo_list.items, i); if (!item.completed) { vector::push_back(&mut pending, item.id); }; i = i + 1; };
pending }}
Sistema de Inventario
Sección titulada «Sistema de Inventario»module 0x42::inventory { use std::vector;
struct Item has store, drop { id: u64, name: vector<u8>, quantity: u64, price: u64 }
struct Inventory has key { items: vector<Item> }
/// Agrega un item al inventario public fun add_item( account: &signer, id: u64, name: vector<u8>, quantity: u64, price: u64 ) acquires Inventory { let addr = std::signer::address_of(account);
if (!exists<Inventory>(addr)) { move_to(account, Inventory { items: vector::empty<Item>() }); };
let inventory = borrow_global_mut<Inventory>(addr);
// Verificar si el item ya existe let (found, index) = find_item_index(&inventory.items, id); if (found) { // Actualizar cantidad existente let existing_item = vector::borrow_mut(&mut inventory.items, index); existing_item.quantity = existing_item.quantity + quantity; } else { // Agregar nuevo item let new_item = Item { id, name, quantity, price }; vector::push_back(&mut inventory.items, new_item); } }
/// Remueve cantidad de un item public fun remove_quantity( account: &signer, item_id: u64, quantity: u64 ) acquires Inventory { let addr = std::signer::address_of(account); let inventory = borrow_global_mut<Inventory>(addr);
let (found, index) = find_item_index(&inventory.items, item_id); assert!(found, 1); // Item no encontrado
let item = vector::borrow_mut(&mut inventory.items, index); assert!(item.quantity >= quantity, 2); // Cantidad insuficiente
item.quantity = item.quantity - quantity;
// Remover item si cantidad es 0 if (item.quantity == 0) { vector::remove(&mut inventory.items, index); } }
/// Función auxiliar para encontrar index de item fun find_item_index(items: &vector<Item>, id: u64): (bool, u64) { let len = vector::length(items); let mut i = 0; while (i < len) { let item = vector::borrow(items, i); if (item.id == id) { return (true, i) }; i = i + 1; }; (false, 0) }
/// Obtiene el valor total del inventario public fun get_total_value(addr: address): u64 acquires Inventory { let inventory = borrow_global<Inventory>(addr); let mut total = 0;
let len = vector::length(&inventory.items); let mut i = 0; while (i < len) { let item = vector::borrow(&inventory.items, i); total = total + (item.quantity * item.price); i = i + 1; };
total }}
Sistema de Votación
Sección titulada «Sistema de Votación»module 0x42::voting { use std::vector;
struct Vote has store, drop { voter: address, choice: u8, timestamp: u64 }
struct Poll has key { question: vector<u8>, options: vector<vector<u8>>, votes: vector<Vote>, end_time: u64, creator: address }
/// Crea una nueva encuesta public fun create_poll( creator: &signer, question: vector<u8>, options: vector<vector<u8>>, duration: u64 ) { let creator_addr = std::signer::address_of(creator); let end_time = timestamp::now_seconds() + duration;
move_to(creator, Poll { question, options, votes: vector::empty<Vote>(), end_time, creator: creator_addr }); }
/// Emite un voto public fun vote(voter: &signer, poll_addr: address, choice: u8) acquires Poll { let voter_addr = std::signer::address_of(voter); let poll = borrow_global_mut<Poll>(poll_addr);
// Verificar que la encuesta no haya terminado assert!(timestamp::now_seconds() <= poll.end_time, 1);
// Verificar que la opción es válida assert!((choice as u64) < vector::length(&poll.options), 2);
// Verificar que el usuario no haya votado ya assert!(!has_voted(&poll.votes, voter_addr), 3);
// Agregar voto let vote = Vote { voter: voter_addr, choice, timestamp: timestamp::now_seconds() }; vector::push_back(&mut poll.votes, vote); }
/// Verifica si un usuario ya votó fun has_voted(votes: &vector<Vote>, voter: address): bool { let len = vector::length(votes); let mut i = 0; while (i < len) { let vote = vector::borrow(votes, i); if (vote.voter == voter) { return true }; i = i + 1; }; false }
/// Obtiene los resultados de la encuesta public fun get_results(poll_addr: address): vector<u64> acquires Poll { let poll = borrow_global<Poll>(poll_addr); let options_count = vector::length(&poll.options); let mut results = vector::empty<u64>();
// Inicializar contadores let mut i = 0; while (i < options_count) { vector::push_back(&mut results, 0); i = i + 1; };
// Contar votos let votes_len = vector::length(&poll.votes); i = 0; while (i < votes_len) { let vote = vector::borrow(&poll.votes, i); let current_count = *vector::borrow(&results, (vote.choice as u64)); *vector::borrow_mut(&mut results, (vote.choice as u64)) = current_count + 1; i = i + 1; };
results }}
Buenas Prácticas
Sección titulada «Buenas Prácticas»1. Verificar Límites de Índice
Sección titulada «1. Verificar Límites de Índice»// ✓ Bueno: verificar antes de accederpublic fun safe_access(v: &vector<u64>, index: u64): u64 { assert!(index < vector::length(v), E_INDEX_OUT_OF_BOUNDS); *vector::borrow(v, index)}
2. Usar Funciones de Utilidad
Sección titulada «2. Usar Funciones de Utilidad»// ✓ Bueno: usar funciones helper para operaciones comunespublic fun find_and_remove<T>(v: &mut vector<T>, item: &T): bool { let (found, index) = vector::index_of(v, item); if (found) { vector::remove(v, index); true } else { false }}
3. Manejar Vectores Vacíos
Sección titulada «3. Manejar Vectores Vacíos»// ✓ Bueno: verificar antes de poppublic fun safe_pop<T>(v: &mut vector<T>): T { assert!(!vector::is_empty(v), E_EMPTY_VECTOR); vector::pop_back(v)}
4. Optimizar Búsquedas
Sección titulada «4. Optimizar Búsquedas»// ✓ Bueno: usar early return en búsquedasfun find_user_index(users: &vector<User>, target_id: u64): (bool, u64) { let len = vector::length(users); let mut i = 0; while (i < len) { if (vector::borrow(users, i).id == target_id) { return (true, i) // early return }; i = i + 1; }; (false, 0)}
5. Usar Tipos Apropiados para Índices
Sección titulada «5. Usar Tipos Apropiados para Índices»// ✓ Bueno: usar u64 para índices (coincide con vector::length)public fun process_range(v: &vector<u64>, start: u64, end: u64) { assert!(start <= end, E_INVALID_RANGE); assert!(end <= vector::length(v), E_OUT_OF_BOUNDS);
let mut i = start; while (i < end) { // procesar vector::borrow(v, i) i = i + 1; }}
6. Evitar Copias Innecesarias
Sección titulada «6. Evitar Copias Innecesarias»// ✓ Bueno: usar referencias cuando sea posiblepublic fun sum_vector(v: &vector<u64>): u64 { let mut sum = 0; let len = vector::length(v); let mut i = 0; while (i < len) { sum = sum + *vector::borrow(v, i); // préstamo, no copia i = i + 1; }; sum}