Golang Go语言中不太明白官方设计slice这种数据结构的初衷在哪?

Golang Go语言中不太明白官方设计slice这种数据结构的初衷在哪?

不太明白 golang 官方设计 slice 这种可变长数据结构的初衷在哪?是速度更快么还是?

为什么不能跟其他语言一样设计的易用一点呢,有没有大佬出来解惑一下的

感觉这个数据结构用起来要非常的小心,如果按照其他语言的模式来思考,就会产生意想不到的结果

比如下面这 2 个案例

https://v2ex.com/t/581264

https://www.zhihu.com/question/27161493


更多关于Golang Go语言中不太明白官方设计slice这种数据结构的初衷在哪?的实战教程也可以访问 https://www.itying.com/category-94-b0.html

30 回复

就是设计的不合理

更多关于Golang Go语言中不太明白官方设计slice这种数据结构的初衷在哪?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


一些 trade off 吧,也没哪里不易用啊,理解这个模型即可,常见用法都是 s = append(s, item), 旧的 s 不再使用

善用指针和 copy。

https://blog.golang.org/go-slices-usage-and-internals

另外,C++ 的 std::vector 也是差不多的结构

你把 golang 当 C 用就明白了

好像一个是数组,一个是切片,数组长度是不能变的,声明多少就是多少,切片是对一个数组中一部分的引用。
写 C++ 的时候就有过这样的情况,你需要一个数组的话,就要在代码里写死长度,或是通过 new 来分配一个固定长度的数组,总之数组一旦创建,长度就不可变,如果容量不够的话,就要重新分配一个数组,然后把旧数据复制过去,然后把旧数组释放掉(代码里写死的长度的数组就不能扩展了)。
Go 其实也是一样的,数组一旦创建长度就不能变,因为向系统申请的连续内存空间已经确定了,后面可能已经存了其他数据,不可以直接越界。
而 Go 为了方便,提供了一个切片(语法上,数组是中括号里要写固定长度,而切片就直接是一对中括号),切片实际上就是个指针,底层需要一个数组来作为存储空间,切片的起始位置一定是在数组头(也可以往后移动,只要不超出数组长度范围就行,但是往后移动了就移不回来了,相当于少了一个存储空间),切片的长度一定小于等于数组长度。以此来提供灵活的数组访问。
在 append 的时候就会出现这样的问题,如果容量不够了,就和 C++ 一样,需要重新创建一个数组,把数据复制过去,只不过这个过程被自动化了,新数组容量我印象中是以两倍的方式来增长的。

其实我觉得如果你知道它的原理,再稍微知道一些 C++ 的东西,就觉得其实也没啥不合理的,知道了原理之后就不会出错了,而且还觉得相比 C++ 还挺好用的。

你的其他语言不会都是些类 script 语言吧?

#5 对比也应该用 vector 来对比吧,看似提供了数组的语法糖却容易让人产生混乱

切片的设计初衷应该是提供一种轻量级的基础数据结构作为语言特性,Go 对于其他重型数据结构的支持则有所欠缺,当然只是做 CRUD 的话也用不到什么太重型的。

哪个高级点的语言不需要一个可变长的数组? 用 cpp 你不用 vector 么,写死自己要

C++的 vector 在按值传递的时候底层数组也会复制一遍,跟 Go 这种虽然是按值传递但是却是一层 shallow copy 的行为还是不太一样吧

Ken Thompson 设计的哟。

其实可变长度的数组是 ok 的,但能不能叫做 DynamicArray 之类的名字,其他语言的程序员也能看得懂嘛。叫 slice 简直不能太傻了

实际叫切片并没有什么问题,slice 底层实现的确是取数组的一个切片。而按照 c 语言的的动态数组来看,一旦确定长度,并不能更改,所以叫 DynamicArray 并不准确。

官方为了切 jj 用的

因为数组长度被设计成了不可变的,所以需要一个更灵活的结构,就像 java C#里面的 string 和 stringbuilder

Golang 还有 nil != nil 呢

std::vector

对比下 rust 的 slice,就会觉得 go 的还不错了。。。

值传递有自己的好处,

跟 slice 有什么关系。也根本不需要关心 slice 的 internal implementation。go 是 pass by value,所以如果要修改数据必须要 pass in pointer。如果别的需要修改数据的 pass in pointer 只有 slice 不用 pass in pointer 就会很 confusing。

看起来有点不伦不类,像 std::span 和 std::vector 的混合体

因为易用和性能是冲突的,那么作为设计者就必然要权衡。

如果一个设计要损失 10%的性能,但是开发效率提升 100%,要不要?
如果一个设计要损失 50%的性能,但是开发效率提升 100%,要不要?

关于用其它语言的思维方式无法理解的问题,那是因为 Go 最初就是设计给 C/C++ 程序员用的。熟悉 C/C++ 思维模式的程序员应该很容易理解这些。

这结论太虚了。从这个这么广的结论,你怎么样也要说『比如 go 的 slice 就为了 xx 的性能而舍弃了 xx 的易用,这是没办法的』

C 语言的那个应该叫变长数组(变量长度数组)吧,并不是动态的。

其内部对 data 的引用变化 扩容时机 是造成困扰的主要原因。

答主被高级语言惯久了~

一个 slice 值是对底层数组的一个固定切片,长度是不可变的,每次改变长度都是生成新的切片。
%p 对于 slice 来说返回的是底层数组的地址,真实的 slice 值的地址需要取地址符&。

在Go语言中,slice(切片)的设计是一个深思熟虑且极其强大的特性,其初衷主要体现在以下几个方面:

  1. 灵活性与效率:slice提供了动态数组的功能,能够根据需要自动扩容,避免了手动管理内存分配和释放的复杂性。同时,slice的底层实现基于数组,保证了在大多数情况下操作的高效性。

  2. 简化编程模型:slice提供了一种简洁而强大的方式来处理序列数据,无论是数字、字符串还是自定义类型,slice都能很好地支持。这使得在Go语言中处理集合数据时,代码更加简洁易读。

  3. 内存安全:通过slice,Go语言提供了一种相对安全的内存访问方式。slice的边界检查机制避免了数组越界的错误,这在其他语言中是一个常见的安全问题。

  4. 函数式编程支持:slice与Go语言的函数式编程特性相得益彰。通过slice,可以方便地传递和处理数据集合,使得在Go语言中实现高阶函数和映射、过滤等操作变得简单自然。

  5. 与底层数组的关联:slice与底层数组之间的关联关系,使得slice在传递和共享数据时更加高效。同时,这种关联也提供了一种机制,使得可以通过slice来访问和修改底层数组的数据。

综上所述,slice在Go语言中的设计初衷是为了提供一种高效、安全、灵活且易于使用的数据结构,以支持Go语言的编程模型和特性。

回到顶部