Skip to content
🎉 Welcome to the new Aptos Docs! Click here to submit an issue.

向量(Vector)

vector<T> 是 Move 语言提供的唯一基本集合类型。vector<T> 是同质化的 T 类型集合,可以通过在”末尾”推入/弹出值来扩展或收缩。

vector<T> 可以用任何类型 T 实例化。例如,vector<u64>vector<address>vector<0x42::MyModule::MyResource>vector<vector<u8>> 都是有效的向量类型。

字面量

通用 vector 字面量

任何类型的向量都可以通过 vector 字面量创建。

语法类型描述
vector[]vector[]: vector<T> 其中 T 是任意单一非引用类型空向量
vector[e1, ..., en]vector[e1, ..., en]: vector<T> 其中 e_i: T 满足 0 < i <= nn > 0包含 n 个元素的向量(长度为 n

在这些情况下,vector 的类型可以从元素类型或向量的使用场景推断出来。如果无法推断类型,或者为了更清晰,可以显式指定类型:

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

vector<u8> 字面量

在 Move 中,向量的一个常见用例是表示”字节数组”,即 vector<u8>。这些值通常用于加密目的,如公钥或哈希结果。这些值非常常见,因此提供了特定语法来提高可读性,而不必使用 vector[] 并以数字形式指定每个单独的 u8 值。

目前支持两种类型的 vector<u8> 字面量:字节字符串十六进制字符串

字节字符串

字节字符串是以 b 为前缀的带引号字符串字面量,例如 b"Hello!\n"

这些是 ASCII 编码的字符串,允许使用转义序列。目前支持的转义序列有:

转义序列描述
\n换行符(或行结束符)
\r回车符
\t制表符
\\反斜杠
\0空字符
\"引号
\xHH十六进制转义,插入十六进制字节序列 HH

十六进制字符串

十六进制字符串是以 x 为前缀的带引号字符串字面量,例如 x"48656C6C6F210A"

每对字节(从 00FF)被解释为十六进制编码的 u8 值。因此每对字节对应结果 vector<u8> 中的一个条目。

字符串字面量示例

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 通过 Move 标准库中的 std::vector 模块提供了多种操作,如下所示。未来可能会添加更多操作。 关于 vector 的最新文档可以在 这里 找到。

Function描述是否中止?
vector::empty<T>(): vector<T>创建一个可以存储类型为 T 的值的空向量永不
vector::is_empty<T>(self: &vector<T>): bool如果向量 self 没有元素则返回 true,否则返回 false永不
vector::singleton<T>(t: T): vector<T>创建一个包含 t 的大小为 1 的向量永不
vector::length<T>(self: &vector<T>): u64返回向量 self 的长度永不
vector::push_back<T>(self: &mut vector<T>, t: T)t 添加到 self 的末尾永不
vector::pop_back<T>(self: &mut vector<T>): T移除并返回 self 中的最后一个元素如果 self 为空
vector::borrow<T>(self: &vector<T>, i: u64): &T返回索引 i 处元素的不可变引用如果 i 越界
vector::borrow_mut<T>(self: &mut vector<T>, i: u64): &mut T返回索引 i 处元素的可变引用如果 i 越界
vector::destroy_empty<T>(self: vector<T>)删除 self如果 self 不为空
vector::append<T>(self: &mut vector<T>, other: vector<T>)other 中的元素添加到 self 的末尾永不
vector::reverse_append<T>(self: &mut vector<T>, other: vector<T>)other 向量中的所有元素以相反的顺序推入 self 向量永不
vector::contains<T>(self: &vector<T>, e: &T): bool如果 e 在向量 self 中则返回 true,否则返回 false永不
vector::swap<T>(self: &mut vector<T>, i: u64, j: u64)交换向量 self 中第 i 个和第 j 个索引处的元素如果 ij 越界
vector::reverse<T>(self: &mut vector<T>)原地反转向量 self 中元素的顺序永不
vector::reverse_slice<T>(self: &mut vector<T>, l: u64, r: u64)原地反转向量 self[l, r) 范围内元素的顺序如果 l > rlr 越界
vector::index_of<T>(self: &vector<T>, e: &T): (bool, u64)如果 e 在向量 self 的索引 i 处则返回 (true, i),否则返回 (false, 0)永不
vector::insert<T>(self: &mut vector<T>, i: u64, e: T)在位置 0 <= i <= length 处插入新元素 e,时间复杂度为 O(length - i)如果 i 越界
vector::remove<T>(self: &mut vector<T>, i: u64): T移除向量 self 的第 i 个元素,并移动所有后续元素。时间复杂度为 O(n),保持向量中元素的顺序如果 i 越界
vector::swap_remove<T>(self: &mut vector<T>, i: u64): T将向量 self 的第 i 个元素与最后一个元素交换并弹出该元素,时间复杂度为 O(1),但不保持向量中元素的顺序如果 i 越界
vector::trim<T>(self: &mut vector<T>, new_len: u64): vector<T>将向量 self 截断为较小的长度 new_len,并按顺序返回被移除的元素如果 new_len > self.length()
vector::trim_reverse<T>(self: &mut vector<T>, new_len: u64): vector<T>将向量 self 截断为较小的长度 new_len,并按相反顺序返回被移除的元素如果 new_len > self.length()
vector::rotate<T>(self: &mut vector<T>, rot: u64): u64原地旋转向量,如 rotate(&mut [1, 2, 3, 4, 5], 2) -> [3, 4, 5, 1, 2],返回分割点(本例中为 3如果不满足 rot <= self.length()
vector::rotate_slice<T>(self: &mut vector<T>, left: u64, rot: u64, right: u64): u64原地旋转切片 [left, right),其中 left <= rot <= right,返回分割点如果不满足 left <= rot <= right <= self.length()

示例:

script {
  use std::vector;
 
  fun example() {
    let v = vector::empty<u64>();
    vector::push_back(&mut v, 5);
    vector::push_back(&mut v, 6);
 
    assert!(*vector::borrow(&v, 0) == 5, 42);
    assert!(*vector::borrow(&v, 1) == 6, 42);
    assert!(vector::pop_back(&mut v) == 6, 42);
    assert!(vector::pop_back(&mut v) == 5, 42);
  }
}

向量的索引表示法

自语言版本2.0起

可以使用方括号 ([]) 进行向量操作索引表示,这简化了语法并使程序更易理解。 索引表示法只是编译器将其转换为现有操作的语法糖;原有的命名操作仍然支持。

下表概述了向量的索引表示法:

索引语法对应的向量操作
&v[i]vector::borrow(&v, i)
&mut v[i]vector::borrow_mut(&mut v, i)
v[i]*vector::borrow(&v, i)
v[i] = x*vector::borrow_mut(&mut v, i) = x
&v[i].field&vector::borrow(&v, i).field
&mut v[i].field&mut vector::borrow_mut(&mut v, i).field
v[i].fieldvector::borrow(&v, i).field
v[i].field = xvector::borrow_mut(&mut v, i).field = x

例如,以下是使用索引表示法的向量冒泡排序算法:

fun bubble_sort(v: vector<u64>) {
  use std::vector;
  let n = vector::length(&v);
  let i = 0;
 
  while (i < n) {
    let j = 0;
    while (j < n - i - 1) {
      if (v[j] > v[j + 1]) {
        let t = v[j];
        v[j] = v[j + 1];
        v[j + 1] = t;
      };
      j = j + 1;
    };
    i = i + 1;
  };
}

销毁和复制向量

vector<T> 的某些行为取决于元素类型 T 的能力。例如,包含没有 drop 能力的元素的向量不能像上面例子中的 v 那样隐式丢弃——必须使用 vector::destroy_empty 显式销毁。

注意 vector::destroy_empty 会在运行时中止,除非 vec 包含零个元素:

script {
  fun destroy_any_vector<T>(vec: vector<T>) {
    vector::destroy_empty(vec) // 删除这行会导致编译器错误
  }
}

但对于包含具有drop能力元素的向量,丢弃时不会出错:

script {
  fun destroy_droppable_vector<T: drop>(vec: vector<T>) {
    // 有效!
    // 不需要显式操作来销毁向量
  }
}

类似地,除非元素类型具有 copy 能力,否则向量不能被复制。换句话说,vector<T> 具有 copy 能力当且仅当 T 具有 copy 能力。

更多细节请参阅 类型能力泛型 部分。

所有权

上文 所述,vector 值可以被复制仅当元素可以被复制时