Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert between slices of different types

Tags:

go

go-reflect

I get a byte slice ([]byte) from a UDP socket and want to treat it as an integer slice ([]int32) without changing the underlying array, and vice versa. In C(++) I would just cast between pointer types; how would I do this in Go?

like image 939
slartibartfast Avatar asked Aug 12 '12 17:08

slartibartfast


People also ask

How to convert string slice to byte?

To convert String to Byte array in Golang, use the byte() function. A byte is an 8-bit unsigned int. The byte() function takes a string as an input and returns the array. In Golang, we often use byte slices.

What is a slice data type?

slice is a composite data type and because it is composed of primitive data type (see variables lesson for primitive data types). Syntax to define a slice is pretty similar to that of an array but without specifying the elements count. Hence s is a slice.

What is the difference between a slice and an array?

The basic difference between a slice and an array is that a slice is a reference to a contiguous segment of an array. Unlike an array, which is a value-type, slice is a reference type. A slice can be a complete array or a part of an array, indicated by the start and end index.

Are slices passed by value?

Everything in Go is passed by value, slices too. But a slice value is a header, describing a contiguous section of a backing array, and a slice value only contains a pointer to the array where the elements are actually stored. The slice value does not include its elements (unlike arrays).


1 Answers

As others have said, casting the pointer is considered bad form in Go. Here are examples of the proper Go way and the equivalent of the C array casting.

WARNING: all code untested.

The Right Way

In this example, we are using the encoding/binary package to convert each set of 4 bytes into an int32. This is better because we are specifying the endianness. We are also not using the unsafe package to break the type system.

import "encoding/binary"  const SIZEOF_INT32 = 4 // bytes  data := make([]int32, len(raw)/SIZEOF_INT32) for i := range data {     // assuming little endian     data[i] = int32(binary.LittleEndian.Uint32(raw[i*SIZEOF_INT32:(i+1)*SIZEOF_INT32])) } 

The Wrong Way (C array casting)

In this example, we are telling Go to ignore the type system. This is not a good idea because it may fail in another implementation of Go. It is assuming things not in the language specification. However, this one does not do a full copy. This code uses unsafe to access the "SliceHeader" which is common in all slices. The header contains a pointer to the data (C array), the length, and the capacity. Instead of just converting the header to the new slice type, we first need to change the length and capacity since there are less elements if we treat the bytes as a new type.

import (     "reflect"     "unsafe" )  const SIZEOF_INT32 = 4 // bytes  // Get the slice header header := *(*reflect.SliceHeader)(unsafe.Pointer(&raw))  // The length and capacity of the slice are different. header.Len /= SIZEOF_INT32 header.Cap /= SIZEOF_INT32  // Convert slice header to an []int32 data := *(*[]int32)(unsafe.Pointer(&header)) 
like image 122
Stephen Weinberg Avatar answered Oct 05 '22 13:10

Stephen Weinberg