简体   繁体   中英

How to pass a pointer to a slice of a specific interface in Go?

I want to achieve polymorfism by passing a pointer to a slice of a speficic interface to a function, and update the slice inside of the function. It works quite well with interface{}

package main

import (
    "fmt"
    "strconv"
)

type valuer interface {
    value() string
}

type myInt int

func (i myInt) value() string {
    return strconv.Itoa(int(i))
}

func values(vals interface{}) {
    res, ok := vals.(*[]myInt)
    if !ok {
        panic("wrong type")
    }
    *res = []myInt{1, 2, 3}
}

func main() {
    var a []myInt
    values(&a)
    for _, b := range a {
        fmt.Println(b.value())
    }
}

Go Playground

However if I try to change interface{} to a pointer to a slice of a specific interface it does not work:

package main

import (
    "fmt"
    "strconv"
)

type valuer interface {
    value() string
}

type myInt int

func (i myInt) value() string {
    return strconv.Itoa(int(i))
}

func values(vals *[]valuer) {
    *vals = []myInt{1, 2, 3}
}

func main() {
    var a []myInt
    values(&a)
    for _, b := range a {
        fmt.Println(b.value())
    }
}

Go Playground

returning an error

./prog.go:19:8: cannot use []myInt literal (type []myInt) as type []valuer in assignment
./prog.go:24:9: cannot use &a (type *[]myInt) as type *[]valuer in argument to values

What am I doing wrong?

From the insights provided by @kostix, I can see that I cannot keep both -- a restriction of a non-empty interface, and a simplicity of passing a pointer of a slice of a concrete type. So if I do want to keep the output as a slice of non-empty interfaces, I can do something of this sort instead:

package main

import (
    "fmt"
    "strconv"
)

type valuer interface {
    value() string
}

type myInt int

func (i myInt) value() string {
    return strconv.Itoa(int(i))
}

func values() []valuer {
    res := make([]valuer, 3)
    c := []myInt{1, 2, 3}
    for i, v := range c {
        res[i] = v
    }
    return res
}

func main() {
    a := values()
    for _, b := range a {
        fmt.Println(b.value())
    }
}

Go Playground

It would keep api simpler to use, and allow polymorphism with non-empty interfaces for the output.

One inconvenience for the users with this approach, they will have to "unbox" members of the slice if they want to use methods of a concrete type that are not specified by the interface.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM