55

I want to convert a fixed size array from a slice:

func gen(bricks []Brick) {
    if len(bricks) == 16 {
        if check(Sculpture{bricks}) {
            var b [16]Brick = bricks[0:16];
        }
     }
}

But this results in:

 cannot use bricks[0:16] (type []Brick) as type [16]Brick in assignment

How to convert a slice into a fixed size array?

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
thi gg
  • 1,969
  • 2
  • 22
  • 47

2 Answers2

115

Edit: as of Go 1.17+, you may be able to use new support for slice-to-array conversions, https://tip.golang.org/ref/spec#Conversions_from_slice_to_array_pointer:

s := make([]byte, 2, 4)
s0 := (*[0]byte)(s)      // s0 != nil
s1 := (*[1]byte)(s[1:])  // &s1[0] == &s[1]
s2 := (*[2]byte)(s)      // &s2[0] == &s[0]
s4 := (*[4]byte)(s)      // panics: len([4]byte) > len(s)

Previous answer for Go 1.16 and below:

You need to use copy:

slice := []byte("abcdefgh")

var arr [4]byte

copy(arr[:], slice[:4])

fmt.Println(arr)

As Aedolon notes you can also just use

copy(arr[:], slice)

as copy will always only copy the minimum of len(src) and len(dst) bytes.

xpt
  • 20,363
  • 37
  • 127
  • 216
Stephan Dollberg
  • 32,985
  • 16
  • 81
  • 107
2

I found a way to solve the problem without allocating any more space - to define a new struct with the same construction as slice and receive the unsafe.Pointer.

type MySlice struct {
    Array unsafe.Pointer
    cap   int
    len   int
}
func main(){
    a := []byte{1, 2, 3, 4}
    fmt.Printf("a before %v, %p\n", a, &a)
    b := (*MySlice)(unsafe.Pointer(&a))
    c := (*[4]byte)(b.Array)
    fmt.Printf("c before %v, %T, %p\n", *c, *c, c)
    a[1] = 5
    fmt.Printf("c after %v, %p\n", *c, c)
    fmt.Printf("a after %v, %p\n", a, &a)
}

the result shows as follows: result

Yiheng
  • 31
  • 2
  • This example is very easy to break. See here: https://play.golang.org/p/iSq4Q9XrRQv – Jonathan Hall Dec 04 '20 at 11:45
  • 1
    Also, [don't paste images of text](https://meta.stackoverflow.com/questions/303812/discourage-screenshots-of-code-and-or-errors). – Jonathan Hall Dec 04 '20 at 11:46
  • 3
    If you really need to do this, don't use your own slice header, just convert the data directly: https://play.golang.org/p/3oNob6k5ynx – JimB Dec 04 '20 at 13:24
  • 1
    @JimB thanks for that. But if you change any element of i, x's value will not change. It seems like a deep copy. – Yiheng Dec 04 '20 at 13:36
  • @Yiheng: yes, because arrays are values in the first place, so assignment itself is a copy. If you don't want a copy, you need to use a pointer, but in that case, why not just use a slice in the first place? https://play.golang.org/p/kF3_46UDbJN – JimB Dec 04 '20 at 13:52
  • @JimB Since I need to use a slice as the key of a map, it is the easiest way to do so in my understand. However, your code is better than mine. Thanks for that, and I learned a lot. – Yiheng Dec 04 '20 at 14:01
  • Can you just use byte arrays from the beginning, so no conversion is necessary? – Jonathan Hall Dec 04 '20 at 14:03
  • @Flimzy I really can't, since all data are from protobuf. – Yiheng Dec 04 '20 at 14:14
  • I'm not intimately familiar with that level of detail, but AFAIK, you can unmarshal a pb message into an arbitrary type, so should be able to unmarshal into byte arrays instead of byte slices. If nothing else, you can probably set the target array to be backed by an explicit array. – Jonathan Hall Dec 04 '20 at 14:26