Non-constant conversions

February 29, 2024

Yesterday we looked at conversions of constants. Let’s now consider the more common conversion scenarios of variables and other non-constant expressions.

Conversions

A non-constant value x can be converted to type T in any of these cases:

  • x is assignable to T.
  • ignoring struct tags (see below), x’s type and T are not type parameters but have identical underlying types.
  • ignoring struct tags (see below), x’s type and T are pointer types that are not named types, and their pointer base types are not type parameters but have identical underlying types.

Let’s look at these last two specifically with examples. Let’s repeat the example from Tuesday:

type User struct {
	Username string `json:"username"`
	Password string `json:"password"`
}

type UserDisplay struct {
	Username string `json:"username"`
	Password string `json:"-"`
}

b := User{Username: "bob", Password: "abracadabra"}
bd := UserDisplay(b) // Valid; identical underlying types (ignoring struct tags)

a := &User{Username: "alice", Password: "open sesame"}
ad := (*UserDisplay)(a) // Valid; pointer types are not named

type UserPointer *User
type UserDisplayPointer *UserDisplay

c := UserPointer(&User{Username: "charlie", Password: "hocus pocus"})
cd := (UserDisplayPointer)(c) // cannot convert c (variable of type UserPointer) to type UserDisplayPointer

If you really want to do this latter conversion, you have to take some extra steps, and use this monstrosity:

cd2 := UserDisplayPointer((*UserDisplay)((*User)(c)))

Tomorrow we’ll finish up with conversions of non-generic types, before venturing into that fun area!

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


Ambiguous conversion expressions

There aren’t many places where Go syntax is ambiguous, but here’s one: Conversions … If the type starts with the operator * or <-, or if the type starts with the keyword func and has no result list, it must be parenthesized when necessary to avoid ambiguity: *Point(p) // same as *(Point(p)) (*Point)(p) // p is converted to *Point <-chan int(c) // same as <-(chan int(c)) (<-chan int)(c) // c is converted to <-chan int func()(x) // function signature func() x (func())(x) // x is converted to func() (func() int)(x) // x is converted to func() int func() int(x) // x is converted to func() int (unambiguous) Of these three ambiguous cases, * is the one you’re most likely to run into, so let’s look at an example.


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.


Case expressions

I’m back live streaming again! Join me in just over an hour! Bring your questions, too! Expression switches … If a case expression is untyped, it is first implicitly converted to the type of the switch expression. For each (possibly converted) case expression x and the value t of the switch expression, x == t must be a valid comparison. In other words, the switch expression is treated as if it were used to declare and initialize a temporary variable t without explicit type; it is that value of t against which each case expression x is tested for equality.

Get daily content like this in your inbox!

Subscribe