从C到Rust:容器,容器,容器,Index和Iterator这两口糖甜不甜?

容器 ------ Vec、HashMap、Index、Iterator

一、C 中的"容器"------没有标准容器库

1.1 C 只有数组,没有容器

C 标准库提供的数据结构只有数组------原始、固定大小、不安全:

c 复制代码
// C 的数组------最基本的容器

int arr[10]; // 固定大小,编译时确定

arr[0] = 42; // 访问元素

int x = arr[10]; // ❌ 越界!编译器不报错

C 中没有 std::vector、没有 std::map、没有 std::set。如果要动态数组,必须手写:

c 复制代码
// C 中的"动态数组"------手写

struct DynArray {

int* data;

size_t size; // 有效元素个数

size_t capacity; // 已分配容量

};

  


struct DynArray* da_new() {

struct DynArray* da = malloc(sizeof(*da));

da->data = malloc(4 * sizeof(int));

da->size = 0;

da->capacity = 4;

return da;

}

  


void da_push(struct DynArray* da, int val) {

if (da->size >= da->capacity) {

da->capacity *= 2;

da->data = realloc(da->data, da->capacity * sizeof(int));

}

da->data[da->size++] = val;

}

  


void da_free(struct DynArray* da) {

free(da->data);

free(da);

}

每次写一个新项目,都要重新写一遍这个模式。 不同的项目可能有不同的命名------DynArrayArrayListVector------没有统一的标准。

1.2 C 没有标准哈希表

c 复制代码
// C 中没有 std::unordered_map

// 如果你想用哈希表:

// 选项 1:手写一个(严重的工程负担)

// 选项 2:使用第三方库(uthash、glib GHashTable)

// 选项 3:用 POSIX hsearch(功能非常有限)

  


// uthash 的例子------需要宏,没有类型安全

#include <uthash.h>

  


struct User {

int id;

char name[32];

UT_hash_handle hh; // 宏需要的内部字段

};

  


struct User* users = NULL;

struct User* u = malloc(sizeof(*u));

u->id = 42;

strcpy(u->name, "Alice");

HASH_ADD_INT(users, id, u); // 宏

C 没有标准化的哈希表。 每个项目要么手写、要么选择第三方库、要么接受 POSIX 有限的 hsearch

1.3 C 中"容器"的问题总结

问题 说明
没有统一容器库 动态数组、哈希表、集合都没有标准实现
每次都要手写 每个项目自己实现一套,命名不同、接口不同
类型不安全 void* 是最常见的"泛型"方式
内存管理手动的 扩容、收缩、释放------全部手动
没有迭代器协议 遍历方式各不相同(索引、指针、回调)
没有统一的访问语法 arr[i] 只对原生数组有效

二、Vec<T> ------ 动态数组

2.1 基本用法

Vec<T> 是 Rust 中最常用的容器,相当于 C 手写的动态数组------但类型安全、自动管理内存:

rust 复制代码
// 创建

let mut v: Vec<i32> = Vec::new(); // 空 Vec

let mut v = vec![1, 2, 3]; // 宏:用初始值创建

  


// 增删

v.push(4); // 尾部追加

v.pop(); // 尾部弹出(返回 Option<i32>)

v.insert(1, 100); // 指定位置插入

v.remove(0); // 指定位置删除

  


// 访问

let x = v[0]; // 通过 Index trait(越界 panic)

let x = v.get(0); // 安全的访问(返回 Option<&T>)

  


// 信息

println!("len: {}, cap: {}", v.len(), v.capacity());

// Vec 自动管理:满了就加倍扩容(类似 C 的 realloc,但自动)

2.2 Vec 的内存管理

rust 复制代码
fn vec_memory() {

let mut v: Vec<i32> = Vec::with_capacity(4);

// v: 容量 = 4, 长度 = 0

// 堆上已分配可以容纳 4 个 i32 的空间

  


v.push(10); // 长度 = 1,不需要扩容

v.push(20); // 长度 = 2

v.push(30); // 长度 = 3

v.push(40); // 长度 = 4

  


v.push(50); // 长度 = 5 > 容量 = 4 → 自动扩容!

// 内部:分配新内存(容量 = 8),复制旧元素,释放旧内存

// 所有元素被 move 到新位置

  


// v 离开作用域 → Vec::drop() 自动释放堆内存

// 不需要手动 free

} // ✅ 自动释放
rust 复制代码
Vec 的内存布局:

  


栈上:             堆上:

┌──────────────┐ ┌────┬────┬────┬────┬────┬────┬────┬────┐
│ Vec<i32>     │ │ 10 │ 20 │ 30 │ 40 │ 50 │    │    │    │
│ ptr ─────────▶ ├────┼────┼────┼────┼────┼────┼────┼────┤
│ len: 5       │ │ ←───  已使用         ──→│←─── 未使用 ──→│
│ cap: 8       │ └───────────────────────────────────────┘
└──────────────┘

对比 C 的手写动态数组------Rust 的 Vec 内部做的是一样的事(malloc / realloc / free),区别在于:

维度 C 手写动态数组 Rust Vec<T>
初始化 手动 malloc + 初始化字段 Vec::new()vec![]
扩容 手动 realloc + 更新容量 push() 自动扩容
释放 手动 free 离开作用域自动 Drop
越界访问 arr[i] 不检查 v[i] 检查边界(越界 panic)
类型安全 void* 或具体类型 Vec<T> 泛型------类型保留

2.3 Vec 的底层就是 C 的动态数组

rust 复制代码
// Vec 的内部结构大致等价于 C 的:

// struct Vec<T> {

// ptr: *mut T, // 指向堆上分配的连续内存

// len: usize, // 已使用的元素个数

// cap: usize, // 已分配的元素容量

// }

Vec 不是魔法------它和你手写的 C 动态数组做同样的事。 只是它把这些事封装得安全、自动、不遗忘。

rust 复制代码
// 获得底层裸指针(与 C 交互时用)

let mut v = vec![1, 2, 3];

let ptr = v.as_mut_ptr(); // *mut i32,和 C 的 int* 一样

let len = v.len();

let cap = v.capacity();

  


// 可以传给 C 函数

// unsafe { c_function(ptr, len); }

2.4 Vec 提供的安全保证

rust 复制代码
// 1. 越界检查(debug 模式 panic,release 模式可优化掉)

let v = vec![1, 2, 3];

// v[100]; // ❌ panic: index out of bounds

  


// 2. 永远不会 double-free

// Vec 是 Drop 的,离开作用域自动释放

// 所有权 move 后原 Vec 失效

  


// 3. 不会使用已释放的内存

let v = vec![1, 2, 3];

let ptr = v.as_ptr();

drop(v); // 显式释放

// println!("{}", ptr); // ❌ 编译错误------v 已移出

三、HashMap<K, V> ------ 哈希表

3.1 基本用法

rust 复制代码
use std::collections::HashMap;

  


let mut scores = HashMap::new();

  


// 插入

scores.insert(String::from("Blue"), 10);

scores.insert(String::from("Yellow"), 50);

  


// 访问

let team = String::from("Blue");

let score = scores.get(&team); // ✅ Option<&i32>

// scores["Blue"]; // ✅ 通过 Index------但可能 panic(key 不存在)

  


// 遍历

for (key, value) in &scores {

println!("{}: {}", key, value);

}

  


// 安全更新

scores.entry(String::from("Blue"))

.or_insert(0); // 如果不存在才插入

3.2 C 中没有标准哈希表

c 复制代码
// C 中如果要哈希表:

// 选项 1:手写(几百行代码,容易有 bug)

// 选项 2:uthash(宏实现,语法不直观)

// 选项 3:glib(外部依赖)

  


// uthash 的例子:

#include <uthash.h>

  


struct User {

int id;

char name[32];

UT_hash_handle hh;

};

  


struct User* users = NULL;

  


// 插入

struct User* u = malloc(sizeof(*u));

u->id = 42;

strcpy(u->name, "Alice");

HASH_ADD_INT(users, id, u); // 宏------不是函数,没有类型检查

  


// 查找

struct User* found;

HASH_FIND_INT(users, &id, found); // found 可能是 NULL

3.3 Rust 的 HashMap 对比 C

维度 C 哈希表(手写/uthash) Rust HashMap<K, V>
类型安全 无(void* 或宏) 有(HashMap<String, i32>
标准库提供
插入语法 HASH_ADD_INT(宏) .insert(key, val)
查找语法 HASH_FIND_INT(宏) .get(&key) 返回 Option<&V>
缺失 key 的处理 返回 NULL(类型不安全) 返回 None(类型安全)
遍历 手写循环 for (k, v) in &map
内存管理 手动 free 每个 entry 容器 Drop 时自动释放

四、Index trait ------ v[i] 的背后

4.1 C 的 arr[i] 只是指针运算

c 复制代码
int arr[10];

arr[3] = 42; // 等价于 *(arr + 3)

  


// C 的 arr[i] 只对原生数组和指针有效

// 对自定义类型(如上面手写的 DynArray)不能用 []

struct DynArray* da = da_new();

// da[0] = 42; // ❌ 不能这样用------DynArray 不是数组

// 只能用函数:da_set(da, 0, 42);

4.2 Rust 的 Index trait

Rust 用 Index trait 让 v[i] 语法适用于所有容器:

rust 复制代码
// Index trait 的定义

pub trait Index<Idx> {

type Output: ?Sized;

fn index(&self, index: Idx) -> &Self::Output;

}

  


// IndexMut trait(允许写入)

pub trait IndexMut<Idx>: Index<Idx> {

fn index_mut(&mut self, index: Idx) -> &mut Self::Output;

}
rust 复制代码
let v = vec![10, 20, 30];

  


// v[1] 实际上展开为:

// *v.index(1) ← 调用 Index trait 的 index 方法

  


// 所以:

println!("{}", v[1]); // 20 → 自动调用 v.index(1)

println!("{}", v.index(1)); // 20 → 显式调用,和上面等价

4.3 Vec 实现了 Index

rust 复制代码
// Vec<i32> 实现了 Index<usize>

// 所以 v[i] 对 usize 有效

  


let v = vec![1, 2, 3];

let x = v[1]; // 2

  


// 边界检查在哪里?

// 在 Index 的实现里------Vec::index() 内部

// pub fn index(&self, index: usize) -> &T {

// assert!(index < self.len()); // ❌ 越界 panic

// unsafe { self.get_unchecked(index) }

// }

4.4 String 也实现了 Index

rust 复制代码
let s = String::from("hello");

  


// String 实现了 Index<Range<usize>>

let sub = &s[0..2]; // "he"------切片

let sub = &s[1..4]; // "ell"

  


// 但 String 没有实现 Index<usize>

// let c = s[0]; // ❌ 编译错误

// 因为 Rust 的 String 是 UTF-8 编码,一个"字符"不一定是一字节

// s.bytes() → 按字节迭代

// s.chars() → 按 Unicode 标量值迭代

4.5 HashMap 实现了 Index

rust 复制代码
use std::collections::HashMap;

  


let mut map = HashMap::new();

map.insert("key".to_string(), 42);

  


let val = map["key"]; // ✅ 通过 Index 访问

// 等价于:*map.index(&"key".to_string())

  


// let val = map["nope"]; // ❌ panic: no entry found for key

// HashMap 的 Index 实现在 key 不存在时会 panic!

// 安全的做法用 .get():

match map.get("nope") {

Some(v) => println!("{}", v),

None => println!("not found"),

}

4.6 Index 让自定义容器也支持 []

rust 复制代码
// 自定义容器------通过实现 Index 支持 [] 语法

struct MyVec<T> {

data: Vec<T>,

}

  


impl<T> Index<usize> for MyVec<T> {

type Output = T;

fn index(&self, index: usize) -> &T {

&self.data[index] // 委托给内部 Vec

}

}

  


let mv = MyVec { data: vec![1, 2, 3] };

println!("{}", mv[1]); // ✅ 2------通过自定义 Index 实现

Index trait 的意义:[] 不再是语言内置的语法,而是可以被任何类型实现的 trait。C 中只能 arr[i],Rust 中任何容器都可以 container[i]


五、Iterator trait ------ for x in v 的背后

5.1 C 的遍历------每种容器不同

c 复制代码
// C 的三种常见遍历方式:

  


// 方式 1:索引(对数组)

int arr[10];

for (int i = 0; i < 10; i++) {

process(arr[i]);

}

  


// 方式 2:指针(对数组)

for (int* p = arr; p < arr + 10; p++) {

process(*p);

}

  


// 方式 3:链表遍历

struct Node* head = /* ... */;

for (struct Node* p = head; p != NULL; p = p->next) {

process(p->data);

}

  


// 方式 4:回调(对某些库)

hashmap_foreach(map, process); // 传入函数指针

每种容器的遍历方式都不同。 没有一个统一的"遍历"接口。

5.2 Rust 的 Iterator trait

rust 复制代码
// Iterator trait 的核心定义

pub trait Iterator {

type Item;

fn next(&mut self) -> Option<Self::Item>;

}

任何实现了 Iterator 的类型都可以用 for 循环遍历:

rust 复制代码
let v = vec![1, 2, 3];

  


// for 循环遍历

for x in &v { // &v 实现了 IntoIterator

println!("{}", x);

}

  


// for 循环实际上展开为:

let mut iter = (&v).into_iter();

while let Some(x) = iter.next() {

println!("{}", x);

}

5.3 IntoIterator ------ for 循环的入口

rust 复制代码
// for 循环要求类型实现 IntoIterator

pub trait IntoIterator {

type Item;

type IntoIter: Iterator<Item = Self::Item>;

fn into_iter(self) -> Self::IntoIter;

}

Vec 的三种迭代方式:

rust 复制代码
let v = vec![1, 2, 3];

  


// 1. for x in &v ------ 迭代不可变引用(最常用)

for x in &v {

// x: &i32,只能读

}

  


// 2. for x in &mut v ------ 迭代可变引用

for x in &mut v {

// x: &mut i32,可以写

*x *= 2;

}

  


// 3. for x in v ------ 消费 Vec(所有权)

for x in v {

// x: i32,v 的所有权已转移,之后不能再用 v

}

5.4 迭代器适配器

Iterator trait 提供了大量方法,可以对序列进行链式操作:

rust 复制代码
let v = vec![1, 2, 3, 4, 5];

  


// 链式操作------编译器优化后和手写循环一样高效

let result: Vec<i32> = v.iter()

.filter(|x| *x % 2 == 0) // 只保留偶数

.map(|x| x * 2) // 每个乘以 2

.collect(); // 收集到 Vec

  


println!("{:?}", result); // [4, 8]

  


// 注意:这是"零成本抽象"------适配器链在编译期被合成为等价的手写循环

// 没有运行时开销

常用适配器:

rust 复制代码
let v = vec![4, 2, 5, 1, 3];

  


// filter ------ 过滤

v.iter().filter(|x| **x > 2); // 只保留 > 2 的元素

  


// map ------ 变换

v.iter().map(|x| x * 2); // 每个元素乘以 2

  


// take ------ 取前 n 个

v.iter().take(3); // 只取前 3 个

  


// skip ------ 跳过 n 个

v.iter().skip(2); // 跳过前 2 个

  


// chain ------ 拼接

v.iter().chain([10, 11].iter()); // 先 v 再 [10, 11]

  


// enumerate ------ 带索引

v.iter().enumerate(); // (index, value) 对

  


// zip ------ 配对

v.iter().zip(["a", "b", "c"].iter()); // (1,"a"), (2,"b"), (3,"c")

  


// fold ------ 折叠

v.iter().fold(0, |acc, x| acc + x); // 求和

  


// all / any ------ 检查

v.iter().all(|x| x > 0); // 所有元素 > 0?

v.iter().any(|x| x == 3); // 有元素 == 3?

  


// find ------ 查找

v.iter().find(|x| **x == 3); // 返回第一个等于 3 的元素

  


// position ------ 找位置

v.iter().position(|x| *x == 3); // 返回 3 的位置(索引)

5.5 迭代器是惰性的

rust 复制代码
let v = vec![1, 2, 3, 4, 5];

  


// 这段代码什么都不做------迭代器是惰性的

let iter = v.iter()

.filter(|x| { println!("filter {}", x); **x % 2 == 0 })

.map(|x| { println!("map {}", x); x * 2 });

  


// 只有调用 .collect() 或 .for_each() 等"消费者"时,才真正执行

let result: Vec<_> = iter.collect(); // 这里才开始计算

// 输出:

// filter 1

// filter 2

// map 2

// filter 3

// filter 4

// map 4

// filter 5

5.6 C 没有等效的迭代器体系

c 复制代码
// C 中实现"链式操作":

int arr[] = {1, 2, 3, 4, 5};

int n = 5;

  


// 步骤 1:过滤偶数

int temp1[5];

int cnt1 = 0;

for (int i = 0; i < n; i++) {

if (arr[i] % 2 == 0) {

temp1[cnt1++] = arr[i];

}

}

  


// 步骤 2:每个乘以 2

int temp2[5];

for (int i = 0; i < cnt1; i++) {

temp2[i] = temp1[i] * 2;

}

// ...... 每个操作都需要一个临时数组、一个循环

// 代码增长了,引入了临时变量,容易出错
rust 复制代码
// Rust ------ 链式操作,无需临时变量

let result: Vec<_> = arr.iter()

.filter(|x| *x % 2 == 0)

.map(|x| x * 2)

.collect();

六、C vs Rust 容器对比

6.1 容器类型对比

| 容器类型 | C | Rust |

|---------|---|------| | 动态数组 | 手写(malloc/realloc/free) | Vec<T> | | 哈希表 | 无标准(uthash/glib) | HashMap<K, V> | | 有序哈希表 | 无 | BTreeMap<K, V> | | 集合 | 无标准 | HashSet<T> / BTreeSet<T> | | 双端队列 | 手写 | VecDeque<T> | | 链表 | 手写 | LinkedList<T> | | 字符串 | char* + <string.h> | String / &str |

6.2 接口对比

操作 C(手写动态数组) Rust Vec<T>
创建 struct DynArray* da = da_new() let v = Vec::new()
追加 da_push(da, val) v.push(val)
删除末尾 需要手写 v.pop()
获取元素 da_get(da, i)da->data[i] v[i](Index trait)
获取长度 da->size v.len()
遍历 for (int i=0; i<da->size; i++) for x in &v(Iterator)
排序 手写或 qsort v.sort()
释放 da_free(da) 自动 Drop

6.3 一个完整的功能:C vs Rust

c 复制代码
// C ------ 用动态数组存用户数据,遍历、过滤

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

  


struct User {

int id;

char name[64];

};

  


struct DynArray {

struct User* data;

size_t size, capacity;

};

  


struct DynArray* da_new() { /* ... */ }

void da_push(struct DynArray* da, struct User u) { /* ... */ }

void da_free(struct DynArray* da) { /* ... */ }

  


int main() {

struct DynArray* users = da_new();

  


struct User u1 = {1, "Alice"};

struct User u2 = {2, "Bob"};

struct User u3 = {3, "Charlie"};

da_push(users, u1);

da_push(users, u2);

da_push(users, u3);

  


// 过滤 id > 1 的用户并打印

for (size_t i = 0; i < users->size; i++) {

if (users->data[i].id > 1) {

printf("%s\n", users->data[i].name);

}

}

  


da_free(users);

return 0;

}
rust 复制代码
// Rust ------ 同样的功能

#[derive(Debug)]

struct User {

id: i32,

name: String,

}

  


fn main() {

let users = vec![

User { id: 1, name: "Alice".into() },

User { id: 2, name: "Bob".into() },

User { id: 3, name: "Charlie".into() },

];

  


// 过滤 id > 1 的用户并打印

users.iter()

.filter(|u| u.id > 1)

.for_each(|u| println!("{}", u.name));

  


// users 离开作用域 → 自动释放 ✅

}

七、与 C 程序员的对话

"Vec 不就是封装了 realloc 吗?"

C 程序员:"Vec 的 push 背后就是 realloc------我手写的动态数组也是这么做的。Vec 只是帮我自动调用了 realloc 而已。"
Rust :"这就是关键------'自动调用'意味着你永远不需要记得调用它。你永远不需要检查 realloc 是否返回 NULL。你永远不需要在不用的时候手动 free。而且 v[i] 有边界检查------C 的 arr[i] 没有。这些'自动'加起来,就是消除了一整个类别的内存 bug。"

c 复制代码
// C ------ 忘了一个检查就是 bug

int* p = realloc(arr, new_size * sizeof(int));

if (p == NULL) { /* 必须检查 */ } // 如果忘了------灾难
rust 复制代码
// Rust ------ Vec 内部处理 OOM(通常 panic)

// 你不需要写检查代码

let mut v = vec![1, 2, 3];

v.push(4); // ✅ 自动扩容,自动处理错误

"哈希表我用了 uthash 很久了,没什么不好"

C 程序员:"uthash 用宏实现的哈希表,我用了很多年,没什么问题。"
Rust :"uthash 是一个优秀的库------但它不是标准的一部分,每个 C 项目需要自己决定用不用、用哪个。而且宏实现的语法不是 Rust 式的函数调用------HASH_ADD_INT(users, id, u)------类型检查有限,宏展开出错难以调试。Rust 的 HashMap 是标准库的一部分,. 调用的语法在任何编辑器里都有自动补全。"

c 复制代码
// C------uthash 的宏

HASH_FIND_INT(users, &id, found);

// 宏:不检查 users 的类型、id 的类型、found 的类型
rust 复制代码
// Rust------方法是类型安全的

let found: Option<&User> = users.get(&id);

// 如果 id 的类型和 key 类型不匹配------编译错误

"Iterator 不就是函数指针回调吗?"

C 程序员 :"Rust 的 for x in &v 让我想起 C 中函数指针的回调遍历,只是语法不一样。"
Rust :"不只是语法。迭代器是惰性的------你可以构建一个操作链(.filter().map()),只有最后调用 .collect() 时才真正执行。C 的函数回调做不到这种惰性组合------每个回调都是立即执行的。而且迭代器适配器链在编译期会被优化成手写循环------零运行时开销。"

c 复制代码
// C ------ 立即执行,不能链式组合

void process_if_even(void* ctx, int val) {

if (val % 2 == 0) {

process(val * 2);

}

}

foreach(arr, n, process_if_even); // 现在就执行了
rust 复制代码
// Rust------惰性组合

let result: Vec<_> = arr.iter()

.filter(|x| *x % 2 == 0) // 只是声明了"要过滤"

.map(|x| x * 2) // 只是声明了"要变换"

.collect(); // 到这里才真正执行

八、小结

8.1 C 的问题

css 复制代码
C 没有标准容器库

→ 动态数组、哈希表、集合都需要手写

→ 每种容器的遍历方式不同

→ 没有统一的访问语法([] 只对原生数组有效)

→ 内存管理全部手动

8.2 Rust 的解决

问题 Rust 的解决
没有标准动态数组 Vec<T>(类型安全、自动扩容、自动释放)
没有标准哈希表 HashMap<K, V>(标准库提供)
没有统一访问语法 Index trait------任何容器都支持 container[i]
没有统一遍历方式 Iterator trait------所有容器都用 for x in &container
内存管理手动 容器类型实现 Drop------离开作用域自动释放

8.3 容器 + Trait 的关系

rust 复制代码
容器(Vec、HashMap、String......)

↓ 实现了

Index trait → container[i] 语法

IntoIterator trait → for x in container 语法

↓

调用者不需要知道容器内部是什么

只需要知道:它实现了 Index + IntoIterator

一句话总结 :C 没有标准容器库------动态数组手写、哈希表第三方、遍历方式各不相同。Rust 的标准容器(VecHashMap 等)通过 Index trait(统一的 [] 访问)和 Iterator trait(统一的 for 遍历)与语言语法深度集成------让"操作容器"和"操作原生数组"一样自然,同时保持类型安全和内存安全。

相关推荐
小杍随笔4 小时前
【Rust全栈项目接入UUID主键实战:从选型到PostgreSQL适配(含v7优化)】
开发语言·postgresql·rust
doiito(Do It Together)18 小时前
media_agent 进化之路:把 Gliding Horse 的 Agent 超能力注入 ComfyUI,让图片生成自己“学会”优化
人工智能·架构·rust·knowledge graph
techdashen1 天前
Arborium:把 tree-sitter 语法高亮打包成 Rust 文档生态的基础设施
开发语言·后端·rust
鹰影471 天前
一款AI笔记助手和远程同步的markdown笔记idea-note
人工智能·笔记·rust·typescript·react
Aspiresky1 天前
探索Rust语言之引用
开发语言·后端·rust
techdashen1 天前
一次 DDoS 之后的 Rust 网站复盘
开发语言·rust·ddos
独孤留白1 天前
从C到Rust:最基础的Trait Sized
rust
techdashen1 天前
把正确性藏进类型里:从 Go 的 io.Reader 到 Rust 的 API 设计
网络·golang·rust
前端之虎陈随易1 天前
Rust、Golang、MoonBit 编译成 WASM,体积和速度差距有多大?
golang·rust·wasm