Other

1 min read


Watch my first ever Boldly Go Live stream

Next Monday, June 19, I’ll be doing my first ever Boldly Go Live stream on YouTube! I’ll be live coding on a real Go project, and answering audience questions. The event will begin at 3:00pm Central European Summer Time (or 13:00 UTC), and I’ll go for up to two hours, depending on the vibe. You can catch the event here. Drop by early, and click the “Notify Me” link, to be reminded when the event starts.


Blocks

Blocks A block is a possibly empty sequence of declarations and statements within matching brace brackets. Block = "{" StatementList "}" . StatementList = { Statement ";" } . The most common example of an explicit block is the body of a function: func foo() { // <--- The body block start here /* some interesting code goes here */ } // <--- and ends here However, you can also create an explicit block at any time, simply by placing brackes/curly braces ({ and }) around some code:


Defining method sets

Recall that every type in Go has a method set. Let’s examine how that’s defined: Method sets … Every type has a (possibly empty) method set associated with it: The method set of a defined type T consists of all methods declared with receiver type T. type S struct { /* Possibly some fields */ } // Foo() is a member of the method set of `S`, because it has a receiver type `S`.


Every type has a method set

Method sets The method set of a type determines the methods that can be called on an operand of that type. Every type has a (possibly empty) method set associated with it. Wait right there. That bears repeating Every type has a (possibly empty) method set Let that sink in. Every type has a method set. This surprises many newcomers, as in many langauges, we’re accustomed to thinking of only “objects” having methods.

Subscribe to Boldly Go: Daily

Every day I'll send you advice to improve your understanding of Go. Don't miss out! I will respect your inbox, and honor my privacy policy.

Unsure? Browse the archive.


Representability

We’ve learned about types, type identity, and type assignability. But you may recall a bit of a discussion about some of the esoteric features of constants in Go. Namely, they can be untyped, and they don’t overflow. So how do we know if a constant can be represented faithfully by a particular type? We finally have the answer, in all its detailed glory: Representability A constant x is representable by a value of type T, where T is not a type parameter, if one of the following conditions applies:


Assignability of type parameters

Let’s finish up our discussion of assignability by looking at how type parameters are special. Assignability … Additionally, if x’s type V or T are type parameters, x is assignable to a variable of type T if one of the following conditions applies: x is the predeclared identifier nil, T is a type parameter, and x is assignable to each type in T’s type set. func foo[T *int | *int32](t T) { t = nil } V is not a named type, T is a type parameter, and x is assignable to each type in T’s type set.


Assignability

Now we get to tie a bunch of the past concepts together in a more concrete way, with assignability. Which types of values can be assigned to which types of variables? That’s what this is all about. It’s mostly intuitive, and in fact, I virtually never find myself wondering about these details while coding. But the details do matter. So let’s look at them, with examples. Assignability A value x of type V is assignable to a variable of type T (“x is assignable to T”) if one of the following conditions applies:

How-Tos

16 min watch


Inspect Your Errors: Unwrapping errors for fun and profit

I've talked about wrapping errors. Now let's talk about the various ways to unwrap and inspect errors.


Type identity wrapup

Today we wrap up our discussion of type identity. This is mostly a recap of what has already been discussed the last few days. Type identity … Given the declarations type ( A0 = []string A1 = A0 A2 = struct{ a, b int } A3 = int A4 = func(A3, float64) *A0 A5 = func(x int, _ float64) *[]string B0 A0 B1 []string B2 struct{ a, b int } B3 struct{ a, c int } B4 func(int, float64) *B0 B5 func(x int, y float64) *A1 C0 = B0 D0[P1, P2 any] struct{ x P1; y P2 } E0 = D0[int, string] ) these types are identical:


Type identities of other types

Type identity … Two pointer types are identical if they have identical base types. Given: type ( P = *int Q = P R *int ) P, Q, and *int are all identical to each other, but different from *int32 and R. Two function types are identical if they have the same number of parameters and result values, corresponding parameter and result types are identical, and either both functions are variadic or neither is.


Type identities of compound types

So we now know that types are either identical or different. let’s look at the details, with examples. Type identity … In detail: Two array types are identical if they have identical element types and the same array length. So given: type ( X = [10]int Y = X Z [10]int ) Types X, Y, and [10]int are identical to each other, but not to [9]int or [10]int32. Type Z is also different from all the others, due to the fact that it is a named type.