V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
echoless
V2EX  ›  Go 编程语言

go append 的疑问

  •  
  •   echoless · 2023-02-27 11:46:28 +08:00 · 1935 次点击
    这是一个创建于 426 天前的主题,其中的信息可能已经有所发展或是发生改变。
    package main
    
    import "fmt"
    
    func main() {
    	// create a slice from an array
    	x := [3]string{"A", "B", "С"}
    	s := x[:] // a slice referencing the storage of x
    	// x[1] = "O"
    	t := append(s, "D") // append items to slice s
    	x[1] = "O"
    	fmt.Println("%+v", x)
    	fmt.Println("%+v", s)
    	fmt.Println("%+v", t)
    }
    

    https://go.dev/play/p/g-eGRJLteAH

    t 为什么是 [A B С D]

    我的理解是 t 在 s 的基础上加了个‘D' , x 改了 s, t 也要跟着变啊.

    41 条回复    2023-02-28 12:40:21 +08:00
    echoless
        1
    echoless  
    OP
       2023-02-27 11:49:50 +08:00
    https://pkg.go.dev/builtin#append

    The append built-in function appends elements to the end of a slice. If it has sufficient capacity, the destination is resliced to accommodate the new elements. If it does not, a new underlying array will be allocated. Append returns the updated slice. It is therefore necessary to store the result of append, often in the variable holding the slice itself:

    If it does not, a new underlying array will be allocated. 草
    nickchenyx
        2
    nickchenyx  
       2023-02-27 11:53:06 +08:00 via Android
    不然 append 为啥会返回一个新 arr 给你持有呢?最好的实践就是一直用最新的 arr 变量,别去动之前的
    echoless
        3
    echoless  
    OP
       2023-02-27 11:53:25 +08:00
    感觉是个坑啊

    ```
    package main

    import "fmt"

    func main() {
    // create a slice from an array
    x := make([]string, 3, 10)
    x[0] = "A"
    x[1] = "B"
    x[2] = "C"
    s := x[:] // a slice referencing the storage of x
    t := append(s, "D") // append items to slice s
    x[1] = "O"
    fmt.Println("%+v", x)
    fmt.Println("%+v", s)
    fmt.Println("%+v", t)
    }
    ````

    https://go.dev/play/p/MLJ9L4o7UQq
    echoless
        4
    echoless  
    OP
       2023-02-27 11:55:08 +08:00   ❤️ 1
    @nickchenyx #2 所谓最佳实践就是用来掩盖语言设计缺陷的.

    从我不多的 golang 经验看, 这个 append 绝对咬过不少人.
    beidounanxizi
        5
    beidounanxizi  
       2023-02-27 11:57:58 +08:00   ❤️ 1
    append 是这样的 里面有个 growslice 扩容机制 memcopy 变成新数组的过程
    1343EFF
        6
    1343EFF  
       2023-02-27 11:58:55 +08:00
    看着像 php 的风格哈哈哈返回一个新的数组而不是让你引用旧的
    echoless
        7
    echoless  
    OP
       2023-02-27 12:01:04 +08:00
    @nickchenyx #2 我以为是在原有 arr 的基础上去扩展.

    看来是

    如果原来的 arr 够用, 就直接在上面扩展
    不够就会新创建一个 arr(把数据 copy 过去).
    echoless
        9
    echoless  
    OP
       2023-02-27 12:03:20 +08:00
    @1343EFF #6 坑在于 如果原来的是容量够, 就是引用旧的. 我觉得每个 golang 程序员都要被咬一次
    echoless
        10
    echoless  
    OP
       2023-02-27 12:04:20 +08:00
    @beidounanxizi #8 多谢, 普通使用要看原理, 说明设计是不够自然.
    Maboroshii
        11
    Maboroshii  
       2023-02-27 12:04:31 +08:00
    是这样的啊,要不然就是 array.append 方法了。而且一般写代码如果这样写,看代码的人都要晕了,一会儿旧一会儿新的。。
    cmdOptionKana
        12
    cmdOptionKana  
       2023-02-27 12:08:02 +08:00   ❤️ 1
    @wuhaoecho 语言怎么设计,有得有失,比如设计成 Java 那样,确实低级坑是少一点,但内存占用就会大。

    Go 希望编译速度快、运行效率高,自然就会要求程序员多费点心思。

    像 C/C++, Rust 之类,低级坑更多,需要程序员耗费精神自己小心处理的地方更多,但运行效率也更高。
    echoless
        13
    echoless  
    OP
       2023-02-27 12:10:14 +08:00
    @cmdOptionKana #12 统一你的观点, 所以我觉得 golang 是比 java 更难的语言, 多了一层 pointer, 弄出来很多“似是而非”的问题.
    cmdOptionKana
        14
    cmdOptionKana  
       2023-02-27 12:15:52 +08:00
    @wuhaoecho 我感觉难度差别很细微,各有各的难点,Java 也有一些很复杂的地方。

    C++, Rust 是难度高得很明显,但如果 Go 和 Java 比,就算说 Go 难,但难那么一点点,几乎可以忽略不计。
    hzjseasea
        15
    hzjseasea  
       2023-02-27 12:16:36 +08:00
    op 真的是边骂边学 golang...
    MoYi123
        16
    MoYi123  
       2023-02-27 12:18:48 +08:00   ❤️ 1
    @wuhaoecho 不是 go 比 java 多了指针, 而是 java 比 go 少了指针.
    echoless
        17
    echoless  
    OP
       2023-02-27 12:19:18 +08:00
    @cmdOptionKana #14 我虽然写的 java 不多, 但是不记得有什么困惑, golang 感觉老搞不清. 虽然也能写, 老觉得这样写是不是符合“标准”, java python 这些从来没有这样的困惑. rust c++ 跟 go java 不是一个层次的, 难是预期之内的.
    echoless
        18
    echoless  
    OP
       2023-02-27 12:21:33 +08:00
    @hzjseasea #15 没办法啊, 生活所迫, 我要是 python 能找到理想的工作才不会学 golang, 不过我把可以骂的点, 都搞清楚了, 就学会了么.
    lesismal
        19
    lesismal  
       2023-02-27 12:25:46 +08:00
    c 时代的 realloc 就是这样的,只是那些语言为了搬砖效率封装了一大堆、然后圈养了大批 CURDer
    hzjseasea
        20
    hzjseasea  
       2023-02-27 12:26:39 +08:00
    @wuhaoecho 我之前也是 python 转的 go,那时候还偶尔看点 rust ,当时给我的感觉就是卧槽这才是代码,python 太多东西给你封装起来 你写代码的时候压根看不见,现在把这些封装的都抛出来让你自己管理了,就觉得这也不好那也不好了... 像 go 的 map slice map channel 这四块都可以去了解下源码, 反正你找工作也要了解的
    echoless
        21
    echoless  
    OP
       2023-02-27 12:27:00 +08:00
    @hzjseasea #15 https://www.v2ex.com/t/919283#reply22

    看了这个我真是...

    这阵容估计只有 c++ 可以一拼了吧.
    lesismal
        22
    lesismal  
       2023-02-27 12:27:09 +08:00   ❤️ 1
    @MoYi123
    > 不是 go 比 java 多了指针, 而是 java 比 go 少了指针.

    这话说的妙极了
    echoless
        23
    echoless  
    OP
       2023-02-27 12:31:59 +08:00
    @hzjseasea #20 大佬你适合去搞汇编
    echoless
        24
    echoless  
    OP
       2023-02-27 12:59:07 +08:00
    https://books.studygolang.com/GoExpertProgramming/chapter01/1.2-slice.html

    https://go.dev/play/p/mgax2-QsRKI

    package main

    import (
    "fmt"
    )

    func AddElement(slice []int, e int) []int {
    return append(slice, e)
    }

    func main() {
    var slice []int
    slice = append(slice, 1, 2, 3)

    newSlice := AddElement(slice, 4)
    fmt.Println(&slice[0] == &newSlice[0])
    }


    专家都搞不清

    我一个新手很快能摸到坑也是不容易.
    Nazz
        25
    Nazz  
       2023-02-27 13:23:22 +08:00 via Android
    设计成 OOP 风格明显会更好,之所以没有大概是因为泛型
    hsfzxjy
        26
    hsfzxjy  
       2023-02-27 13:31:40 +08:00 via Android
    学过 c 的就感觉很自然,类似 realloc 的行为
    echoless
        27
    echoless  
    OP
       2023-02-27 13:33:28 +08:00 via Android
    @lesismal less is more go 语言的爹经常说的
    leonshaw
        28
    leonshaw  
       2023-02-27 13:43:32 +08:00
    像 rust 的 ownership 一样,自己制定一些规则,例如只能通过一个 owner slice append ,append 以后其它相关 slice 都失效。
    zagfai
        29
    zagfai  
       2023-02-27 14:28:35 +08:00
    @lesismal 为什么觉得是圈养了 curder 而不是技术发展提高了生产力了?现在没人钻木取火了吧?
    lanlanye
        30
    lanlanye  
       2023-02-27 14:29:01 +08:00
    因为 slice 底下是固定大小的数组,不够了要扩容+copy ,你可以试试用 make 来指定底层数组的大小,只要不超过这个大小就不会出现问题。
    但归根结底,不知道这个就容易踩坑。
    lesismal
        31
    lesismal  
       2023-02-27 14:29:54 +08:00
    @wuhaoecho
    go 爹说的没毛病,我的这个 id (les is mal)也是 less is more 缩写拼接变换得到的
    很多人嘲讽 go 大道至简,殊不知是他们习惯了搬砖的工作、而 go 不是只为了简单搬砖。。。
    lesismal
        32
    lesismal  
       2023-02-27 14:31:08 +08:00
    先走出自己的舒适区,然后不知不觉就破境了
    lesismal
        33
    lesismal  
       2023-02-27 14:51:54 +08:00
    @zagfai
    你看 #19 我那句的完整顺序:
    1. 先说的 “为了搬砖效率” —— 这个就是提高了部分生产力,因为提高的主要是开发效率 /速度、性能和软硬件消耗的成本是不划算的
    2. 然后才说的“圈养了大批 CURDer”

    科技线的演化规律通常是不同技术潮涨潮落逐步更迭到更好的,相比于 java ,go 的性能和消耗更友好,目前在一些其他语言舒适区使用者眼里,go 开发效率差很多,但毕竟出生的晚,随着逐步完善、开发效率越来越高,而且就我自己而言从来没觉得用 go 比用其他语言开发效率低。

    但 go 性能不够强,只能做第二梯队、在开发效率与性能消耗之前均衡,在 CURD 与基础设施以及这两者的一些中间过渡领域会有很多作为。

    往远一点看,rust 会大量占市场,目前阶段是 rust 已经进入,比如 linux 内核,比如 tidb 这种搞数据库的,比如 cloudflare 的一些基础设施:
    https://mp.weixin.qq.com/s/1dB4P54tVz-aM2iYIkE4fg

    再往远一点看,AI 的发展,未来大部分代码可能会是由 AI 直接生成更高性能的机器码,等到 NLP 、AI 编码更牛逼了,人类需求直接丢给 AI 了,配合上更丰富完善的测试验收体系。全盘丢给 AI 怕它作恶像终结者那样反噬人类,所以你看,OpenAI 的核心宗旨在于“实现安全的通用人工智能(AGI)”,使其有益于人类

    性能是效率的永恒核心,是生产力的核心,现阶段你觉得够用了,并不代表其他人、next gen 也觉得性能够用。所以不要觉得 java 那些提高了生产力就没必要 go 和 rust 了,那只是 CURDer 这些不需要性能的人在坐井观天或者自欺欺人罢了
    lesismal
        34
    lesismal  
       2023-02-27 14:57:00 +08:00
    @zagfai
    go rust 或者 c/cpp ,不是钻木取火。
    如果这些是钻木取火,未来 AI 写代码成熟了,那时候的人同样也会说用 java php 这些是钻木取火、谁还自己写代码啊!?

    过去这十几年,IT 这条线发展太快了,不知道 AI 迭代的速度会有多快,有生之年是否能见识到机器生命雏形甚至更高阶一点:joy:
    pkoukk
        35
    pkoukk  
       2023-02-27 18:39:23 +08:00   ❤️ 1
    不扩容之前,slice 指向底层的 array 不会变,扩容之后就变了
    所以不建议 x := [3]string{"A", "B", "С"}这么用
    一开始就 x:=[]string{},之后 append 也是 x=append(x,...)
    echoless
        36
    echoless  
    OP
       2023-02-28 10:04:32 +08:00
    @pkoukk #35 https://go.dev/play/p/tMshcyKhLSU 用 x := []string{"A", "B", "С"} 结果是一样的.
    pkoukk
        37
    pkoukk  
       2023-02-28 10:28:24 +08:00
    @echoless 你别光看前半句不看后半句啊....
    echoless
        38
    echoless  
    OP
       2023-02-28 10:37:47 +08:00
    @pkoukk #37 哈哈 你的观点楼上有人提过, 就是要最佳实践, 其实就是把负担留给程序员.

    问题是这玩意可是面试官, 考试最喜欢的.

    反正这个 append 我的坑 我是踩过了.
    zagfai
        39
    zagfai  
       2023-02-28 12:31:36 +08:00   ❤️ 1
    @lesismal 别误会。。我不是说 java 很好,我不仅说 go 是钻木取火,java ,rust 也是。连无穷整型都不是 builtin 的,都是比胶水 python 低一个层次的抽象。
    性能和开发效率的极致得占一个吧~而不是像 go
    echoless
        40
    echoless  
    OP
       2023-02-28 12:37:50 +08:00
    @zagfai #39 现在这些语言就是 features 取舍的组合, go 我觉得就是宣传过头, 做一些 infra 的工具还行. 大公司做一些高并发啥的也可以理解, 小公司跟风上 go 去写 crud 感觉是无法理解的(动不动 BAT 谁谁都用了, nnd 是处理一样的问题么).
    echoless
        41
    echoless  
    OP
       2023-02-28 12:40:21 +08:00
    哪天 chatgpt, copilot 能写代码的时候, 估计又有高级程序员跳出来说, xxx 圈养了一堆 crud 都不懂, 只会 prompt 的 XX.
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   3581 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 31ms · UTC 10:40 · PVG 18:40 · LAX 03:40 · JFK 06:40
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.