不太明白 golang 官方设计 slice 这种数据结构的初衷在哪? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
Buffer2Disk
V2EX    Go 编程语言

不太明白 golang 官方设计 slice 这种数据结构的初衷在哪?

  •  
  •   Buffer2Disk 2019-07-09 21:48:39 +08:00 6573 次点击
    这是一个创建于 2355 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

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

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

    比如下面这 2 个案例

    https://v2ex.com/t/581264

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

    第 1 条附言    2019-07-10 10:43:58 +08:00
    楼下有些人可能没有 catch 到 point,我的意思是为什么没有设计的更加易用一点

    slice 在作为函数传参或者 cap 发生扩容的时候,如果按照 Java 或者 Python 的用法,变量使用上就会产生一些迷惑行为,

    上面两个链接的例子就能很好的解释。



    确实在理解原理后,可以正确使用这个数据结构,
    比如函数传参的时候,我传指针进去;
    cap 发生扩容的情况,我始终都是赋值给同一个变量

    但是这样做感觉对于写程序的人就不是那么的易用或者灵活性
    29 条回复    2019-07-15 14:58:26 +08:00
    trait
        1
    trait  
       2019-07-09 23:58:16 +08:00 via iPhone
    就是设计的不合理
    BOYPT
        2
    BOYPT  
       2019-07-10 00:08:17 +08:00
    一些 trade off 吧,也没哪里不易用啊,理解这个模型即可,常见用法都是 s = append(s, item), 旧的 s 不再使用
    Vegetable
        3
    Vegetable  
       2019-07-10 00:09:44 +08:00
    善用指针和 copy。
    reus
        4
    reus  
       2019-07-10 00:10:02 +08:00
    https://blog.golang.org/go-slices-usage-and-internals

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

    其实我觉得如果你知道它的原理,再稍微知道一些 C++ 的东西,就觉得其实也没啥不合理的,知道了原理之后就不会出错了,而且还觉得相比 C++ 还挺好用的。
    cest
        7
    cest  
       2019-07-10 00:44:10 +08:00
    你的其他语言不会都是些类 script 语言吧?
    lynskylate
        8
    lynskylate  
       2019-07-10 00:51:59 +08:00 via Android
    @jinliming2 #5 对比也应该用 vector 来对比吧,看似提供了数组的语法糖却容易让人产生混乱
    nethard
        9
    nethard  
       2019-07-10 01:31:05 +08:00 via iPhone
    切片的设计初衷应该是提供一种轻量级的基础数据结构作为语言特性,Go 对于其他重型数据结构的支持则有所欠缺,当然只是做 CRUD 的话也用不到什么太重型的。
    yegle
        10
    yegle  
       2019-07-10 02:23:01 +08:00
    nullprogram.com/blog/2019/06/30/ 看这个可能方便理解
    bobuick
        11
    bobuick  
       2019-07-10 06:39:40 +08:00
    哪个高级点的语言不需要一个可变长的数组? 用 cpp 你不用 vector 么,写死自己要
    trn4
        12
    trn4  
       2019-07-10 06:53:31 +08:00
    @reus C++的 vector 在按值传递的时候底层数组也会复制一遍,跟 Go 这种虽然是按值传递但是却是一层 shallow copy 的行为还是不太一样吧
    kidlj
        13
    kidlj  
       2019-07-10 07:10:15 +08:00 via iPhone   1
    Ken Thompson 设计的哟。
    ericgui
        14
    ericgui  
       2019-07-10 08:58:48 +08:00 via Android
    其实可变长度的数组是 ok 的,但能不能叫做 DynamicArray 之类的名字,其他语言的程序员也能看得懂嘛。叫 slice 简直不能太傻了
    myyou
        15
    myyou  
       2019-07-10 10:09:50 +08:00
    @ericgui 实际叫切片并没有什么问题,slice 底层实现的确是取数组的一个切片。而按照 c 语言的的动态数组来看,一旦确定长度,并不能更改,所以叫 DynamicArray 并不准确。
    cokyhe
        16
    cokyhe  
       2019-07-10 10:33:49 +08:00
    官方为了切 jj 用的
    ThanksSirAlex
        17
    ThanksSirAlex  
       2019-07-10 11:06:29 +08:00
    因为数组长度被设计成了不可变的,所以需要一个更灵活的结构,就像 java C#里面的 string 和 stringbuilder
    tiedan
        18
    tiedan  
       2019-07-10 11:11:04 +08:00
    Golang 还有 nil != nil 呢
    skadi
        19
    skadi  
       2019-07-10 11:16:05 +08:00
    std::vector
    ensonmj
        20
    ensonmj  
       2019-07-10 12:25:30 +08:00
    对比下 rust 的 slice,就会觉得 go 的还不错了。。。
    swulling
        21
    swulling  
       2019-07-10 12:31:42 +08:00 via iPhone
    值传递有自己的好处,
    calease
        22
    calease  
       2019-07-10 12:53:12 +08:00
    跟 slice 有什么关系。也根本不需要关心 slice 的 internal implementation。go 是 pass by value,所以如果要修改数据必须要 pass in pointer。如果别的需要修改数据的 pass in pointer 只有 slice 不用 pass in pointer 就会很 confusing。
    sosilver
        23
    sosilver  
       2019-07-10 13:04:03 +08:00 via Android
    看起来有点不伦不类,像 std::span 和 std::vector 的混合体
    fcten
        24
    fcten  
       2019-07-10 13:13:54 +08:00
    因为易用和性能是冲突的,那么作为设计者就必然要权衡。

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

    关于用其它语言的思维方式无法理解的问题,那是因为 Go 最初就是设计给 C/C++ 程序员用的。熟悉 C/C++ 思维模式的程序员应该很容易理解这些。
    ChristopherWu
        25
    ChristopherWu  
       2019-07-10 22:20:40 +08:00
    @fcten 这结论太虚了。从这个这么广的结论,你怎么样也要说『比如 go 的 slice 就为了 xx 的性能而舍弃了 xx 的易用,这是没办法的』
    daryl
        26
    daryl  
       2019-07-11 10:31:07 +08:00
    @myyou C 语言的那个应该叫变长数组(变量长度数组)吧,并不是动态的。
    buzailianxi
        27
    buzailianxi  
       2019-07-11 11:44:04 +08:00
    其内部对 data 的引用变化 扩容时机 是造成困扰的主要原因。
    scipio
        28
    scipio  
       2019-07-12 13:57:03 +08:00
    答主被高级语言惯久了~
    webee
        29
    webee  
       2019-07-15 14:58:26 +08:00
    一个 slice 值是对底层数组的一个固定切片,长度是不可变的,每次改变长度都是生成新的切片。
    %p 对于 slice 来说返回的是底层数组的地址,真实的 slice 值的地址需要取地址符&。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2216 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 32ms UTC 16:02 PVG 00:02 LAX 08:02 JFK 11:02
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86