How to convert slice to fixed size array?

45,090

Solution 1

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.

Solution 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

Share:
45,090

Related videos on Youtube

thi gg
Author by

thi gg

my about-me is currently blank

Updated on July 09, 2022

Comments

  • thi gg
    thi gg almost 2 years

    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?

  • LemurFromTheId
    LemurFromTheId almost 9 years
    This is correct, but a small addition: you don't need copy(arr[:], slice[:4]), copy(arr[:], slice) is enough. copy automatically limits itself to the smaller of the given slices.
  • Stephan Dollberg
    Stephan Dollberg almost 9 years
    @Aedolon Thanks for the hint, added it to the answer.
  • Shudipta Sharma
    Shudipta Sharma over 4 years
    nice answer, really.
  • Flimzy
    Flimzy over 3 years
    This example is very easy to break. See here: play.golang.org/p/iSq4Q9XrRQv
  • Flimzy
    Flimzy over 3 years
  • JimB
    JimB over 3 years
    If you really need to do this, don't use your own slice header, just convert the data directly: play.golang.org/p/3oNob6k5ynx
  • Yiheng
    Yiheng over 3 years
    @JimB thanks for that. But if you change any element of i, x's value will not change. It seems like a deep copy.
  • JimB
    JimB over 3 years
    @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? play.golang.org/p/kF3_46UDbJN
  • Yiheng
    Yiheng over 3 years
    @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.
  • Flimzy
    Flimzy over 3 years
    Can you just use byte arrays from the beginning, so no conversion is necessary?
  • Yiheng
    Yiheng over 3 years
    @Flimzy I really can't, since all data are from protobuf.
  • Flimzy
    Flimzy over 3 years
    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.
  • Chris Smowton
    Chris Smowton over 2 years
  • Stephan Dollberg
    Stephan Dollberg over 2 years
    @ChrisSmowton please either edit that to my answer or post a new one as if there is a newer way then that should be in a proper answer and not just a comment.