Saltearse al contenido

Vector

vector<T> es el único tipo de colección primitiva proporcionado por Move. Un vector<T> es una colección homogénea de Ts 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.

Los vectores de cualquier tipo pueden ser creados con literales vector.

SintaxisTipoDescripción
vector[]vector[]: vector<T> donde T es cualquier tipo único, no-referenciaUn vector vacío
vector[e1, ..., en]vector[e1, ..., en]: vector<T> donde e_i: T tal que 0 < i <= n y n > 0Un 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>
script {
fun example() {
(vector[]: vector<bool>);
(vector[0u8, 1u8, 2u8]: vector<u8>);
(vector<u128>[]: vector<u128>);
(vector<address>[@0x42, @0x100]: vector<address>);
}
}

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.

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 EscapeDescripción
\nNueva línea (o Line feed)
\rRetorno de carro
\tTab
\\Barra invertida
\0Null
\"Comilla
\xHHEscape hex, inserta la secuencia de bytes hex HH

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.

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
);
}
}

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í.

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
}
}
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]
}
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
};
}
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
}
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
}
}
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
}
}
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
}
}
// ✓ Bueno: verificar antes de acceder
public fun safe_access(v: &vector<u64>, index: u64): u64 {
assert!(index < vector::length(v), E_INDEX_OUT_OF_BOUNDS);
*vector::borrow(v, index)
}
// ✓ Bueno: usar funciones helper para operaciones comunes
public 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
}
}
// ✓ Bueno: verificar antes de pop
public fun safe_pop<T>(v: &mut vector<T>): T {
assert!(!vector::is_empty(v), E_EMPTY_VECTOR);
vector::pop_back(v)
}
// ✓ Bueno: usar early return en búsquedas
fun 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)
}
// ✓ 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;
}
}
// ✓ Bueno: usar referencias cuando sea posible
public 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
}