Rust的Vector vs. Golang的Slice比较


我的网站是用Go编写的,当程序启动时,它会读取 "blog/"文件夹的内容,并存储所有文本文件的路径,解析所有内容,将每个文件转换为Post结构struct,并追加到Post切片slice中。

我最近开始学习Rust,在阅读这本书时,我注意到切片的工作方式与Go不同,而是使用Vectors。
不明白这到底是为什么?

Go的slice同时包涵了Rust的Vec和Rust的 "slice"
在Go中,[]T涵盖了 "可扩展的存储 "和 "可在恒定时间内创建的对另一个slice的引用"。
在Rust中,这些情况被分割开来。如果你有一个Vec,那么你知道你总是可以添加到它。你对它有所有权。如果你有一个&[T](甚至是一个&mut[T]),那么你所知道的就是它是一个长度和一个指向某个连续的内存区域的指针。你无法扩展它。
同样地,如果你有一个Vec,你不能创建另一个Vec来指向第一个Vec。它必须是一个复制副本。

为什么Go和Rust使用相同的词"slice",但概念不同?
一个潜在的更有趣的问题是,"为什么Rust将Go的slice概念分成两个不同的类型?"
只有一种类型在概念上不是更简单吗?
这真的涉及到语言设计,但我认为这真的可以归结为两件事:

  1. Rust有所有权,没有垃圾收集器。
  2. Go则没有所有权的概念。

在Go中给定一个[]T,你不知道你是否拥有它。你不知道---至少从类型系统来看---你是否被允许对它进行变异而不产生UB。
在Rust中,这两个概念几乎必须在所有权的基础上被分割开来。

然而,你可以通过引用计数在Rust中实现类似Go的slice。(这有点像字节箱所做的,只是它只适用于Vec<u8>,而不是Vec<T>。) 问题是,引用计数会增加开销,而给Vec这样的核心基元增加开销并不是好事。
在Go中,这种开销是完全没有问题的,因为它是一种GC语言,而且GC的使用是无处不在的。它首先是内置于使用该语言的成本中的。

所以,就像我说的,Rust在这里要复杂一些,因为它的语言设计的目标所带来的限制。

好了,又上升了一个层次,回到了问题上:为什么对同一个概念使用不同的名字?
C语言的void和Haskell的void是两个完全不同的东西。有时 "垃圾收集 "包括引用计数,有时不包括。"正则表达式 "实际上可能意味着 "看起来像正则表达式的东西,但实际上可以描述比正则语言更多的东西。"

毫不含糊和精确是有代价的。语境上下文就是一切。
你不能假设在一种编程语言中用来描述一个特定概念的词在其他每一种编程语言中都会以同样的方式使用。
事实上,通常的情况是,这些概念是如此的相似和重叠,以至于很难不假设这样的事情,也许就像Go和Rust中的 "slice "一样。但是,一旦你熟悉了不同的编程语言可能用微妙的不同的词来描述同一个概念的想法,我想你至少会对它不那么吃惊了。