如何编写pop()函数

a := []int{1,2,3}
x, a := a[len(a)-1], a[:len(a)-1]
fmt.Println(a,x)

How to create a pop() function that will do the same for any type of an array?

Here is what I came up with so far:

func pop(a []*interface{}) interface{}{
    x := a[len(a)-1]
    a = a[:len(a)-1]
    return x
}

func main(){
    a := []int{1,2,3}
    x = pop(a)
    fmt.Println(a,x)  // ->  [1,2] 3
}

But I get cannot use a (type []int) as type []interface {}or other error messages if I try to tweak the code by trial and error.

package main

import (
    "fmt"
    "reflect"
)

func pop(a interface{}) interface{} {
    v := reflect.ValueOf(a).Elem()
    x := v.Index(v.Len() - 1)
    v.SetLen(v.Len() - 1)
    return x
}

func main() {
    a := []int{1, 2, 3}
    x := pop(&a)
    fmt.Println(a, x) // ->  [1,2] 3
}

Though this can be implemented, I still think that x, a = a[len(a)-1], a[:len(a)-1] should be better than a pop function.

The go type system doesn't allow you to cast from []type1 -> []type2. Even if it did interfaces are a struct containing a type id and pointer to the object, where normally you would just have the object. Because of this you need to take a interface{} and use reflect to do the slicing.

func pop(slice interface{}) (interface{}, interface{}) {
    v := reflect.ValueOf(slice)
    return v.Slice(0,v.Len()-1).Interface(), v.Index(v.Len()-1).Interface()
}

Go Playground

Note that this loses compile time type safety, because it must use an interface. Additionally, due to using interfaces the poped value may be allocated, creating extra GC pressure.

Common Go style typically recommends not writing a function like this, and just inlining the small amount of code manually.

After all that really good anwers using reflection I also want to add one answer which offers a more idiomatic Go solution. Like Rob Pike said in his great talk about Go Proverbs

  • interface{} says nothing
  • Reflection is never clear

So there should be also one answer showing the idiomatic Go way. This solution does not work for slices of standard types. But there the answer of cshu shows the best solution: x, a = a[len(a)-1], a[:len(a)-1]

For own defined types we have to define a Poper interface and the Pop function takes that as input and returns an empty interface.

type Poper interface {
    Pop() interface{}
}

type MyType struct {
    a []int
}

func (mt *MyType) Pop() interface{} {
    x := mt.a[len(mt.a)-1]
    mt.a = mt.a[:len(mt.a)-1]
    return x
}

func Pop(p Poper) interface{} {
    return p.Pop()
}

func main() {
    a := &MyType{[]int{1, 2, 3}}
    fmt.Println(Pop(a), a)
}

https://play.golang.org/p/UbDkoVYSMA

At all it is not a good idea to return an empty interface, because all following code has to support the interface{}.

The following code example does not work:

func main() {
    a := &MyType{[]int{1, 2, 3}}
    fmt.Println(Pop(a), a)
    var b int
    b = Pop(a)
}

https://play.golang.org/p/wg9__O44A8

The error says everything about that problem: cannot use Pop(a) (type interface {}) as type int in assignment: need type assertion

So the Pop() function does work by returning interface{} but the rest of the code using the result of that function needs to make a type assertion. So if you can avoid it you should search for another solution using types.