Non-constant conversions, part 2

March 1, 2024

Today we’ll finish the list of non-constant conversion rules that don’t relate to type parameters.

Conversions

  • x’s type and T are both integer or floating point types.
  • x’s type and T are both complex types.
  • x is an integer or a slice of bytes or runes and T is a string type.
  • x is a string and T is a slice of bytes or runes.
  • x is a slice, T is an array [Go 1.20] or a pointer to an array [Go 1.17], and the slice and array types have identical element types.

The last one is the only one that feels to me like it needs more than a superficial explanation. So let’s talk about that, with some examples of related concepts.

var s = []int{1, 2, 3, 4, 5}

a := [5]int(s) // Valid
b := [3]int(s) // Valid, b contains the first 3 elements of a
c := [10]int(s) // panic: runtime error: cannot convert slice with length 5 to array or pointer to array with length 10
d := [5]int64(s) // cannot convert s (variable of type []int) to type [5]int64

// And you can convert directly to a pointer of a slice
p := (*[5]int)(s) // Valid

// Print out some results for comparison:
fmt.Println(s, a, p) // [1 2 3 4 5] [1 2 3 4 5] &[1 2 3 4 5]

But notice, when using a pointer to an array, as one might expect, we have multiple variables accessing the same underlying memory. This means that if we change an element of s, the corresponding element pointed to by p will also change. But not in a or b, as these are copies of the individual elements:

s[2] = 99
fmt.Println(s, a, p) // [1 2 99 4 5] [1 2 3 4 5] &[1 2 99 4 5]

Quotes from The Go Programming Language Specification Language version go1.22 (Feb 6, 2024)


Share this

Direct to your inbox, daily. I respect your privacy .

Unsure? Browse the archive .

Related Content


Conversions between numeric types

So we’ve gone through the high-level conversion stuff… now we dive into some particulars. Today, numeric conversions. Conversions between numeric types For the conversion of non-constant numeric values, the following rules apply: When converting between integer types, if the value is a signed integer, it is sign extended to implicit infinite precision; otherwise it is zero extended. It is then truncated to fit in the result type’s size. For example, if v := uint16(0x10F0), then uint32(int8(v)) == 0xFFFFFFF0.


Conversions from slice to array or array pointer

Conversions from slice to array or array pointer … Converting a slice to an array yields an array containing the elements of the underlying array of the slice. Similarly, converting a slice to an array pointer yields a pointer to the underlying array of the slice. In both cases, if the length of the slice is less than the length of the array, a run-time panic occurs. s := make([]byte, 2, 4) a0 := [0]byte(s) a1 := [1]byte(s[1:]) // a1[0] == s[1] a2 := [2]byte(s) // a2[0] == s[0] a4 := [4]byte(s) // panics: len([4]byte) > len(s) s0 := (*[0]byte)(s) // s0 !


Type Conversion & struct tgs

We’ve already mentioned that struct tags are ignored when doing conversion between struct types. Now we see where that’s defined, along with an example: Conversions … Struct tags are ignored when comparing struct types for identity for the purpose of conversion: type Person struct { Name string Address *struct { Street string City string } } var data *struct { Name string `json:"name"` Address *struct { Street string `json:"street"` City string `json:"city"` } `json:"address"` } var person = (*Person)(data) // ignoring tags, the underlying types are identical I don’t think there’s much else to say about that, so let’s move on, and finish up this section.

Get daily content like this in your inbox!

Subscribe