Using type assertions in Go with concrete types

Featured image for sharing metadata for article

As part of working to fix a bug with oapi-codegen today, I've been trying to work out how to call the String() method on a type, if it's set, without needing to use Reflection.

To start with, we're trying to covert a UUID type (from Google's UUID library) to a string, from the following generated code:

// via github.com/google/uuid
type UUID = byte[16]

// String returns the string form of uuid, xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
// , or "" if uuid is invalid.
func (uuid UUID) String() string {
	var buf [36]byte
	encodeHex(buf[:], uuid)
	return string(buf[:])
}

// and then in oapi-codegen generated code

// PostPetTextBody defines parameters for PostPet.
type PostPetTextBody = UUID

// PostPetTextRequestBody defines body for PostPet for text/plain ContentType.
type PostPetTextRequestBody = PostPetTextBody

// NewPostPetRequestWithTextBody calls the generic PostPet builder with text/plain body
func NewPostPetRequestWithTextBody(server string, body PostPetTextRequestBody) (*http.Request, error) {
	var bodyReader io.Reader
	bodyReader = strings.NewReader(body)
	return NewPostPetRequestWithBody(server, "text/plain", bodyReader)
}

This is failing to compile, as the byte[16] can't be implicitly converted to a string.

The best option here is that we'd like to write something like:

	// https://pkg.go.dev/fmt#Stringer
	if stringer, ok := body.(fmt.Stringer) ; ok {
		bodyReader = strings.NewReader(stringer.String())
	}

However, this hits a compilation error because we've got a concrete type (byte[16]) that isn't an interface:

./client.gen.go:130:16: invalid operation: body (variable of array type PostPetTextRequestBody) is not an interface

So how do we use a concrete type as if it's an interface? I'm intentionally trying to avoid (expensive) Reflection calls, so that removes that as an option πŸ€” - after a bit of noodling, I gave up and asked Claude Sonnet 3.7 Thinking, which replied with:

    var bodyStr string
    if stringer, ok := interface{}(body).(fmt.Stringer); ok {
        bodyStr = stringer.String()
    } else {
        bodyStr = fmt.Sprintf("%v", body)
    }
	bodyReader = strings.NewReader(bodyStr)

The crux of it is this change to the code:

-	if stringer, ok := body.(fmt.Stringer) ; ok {
+	if stringer, ok := interface{}(body).(fmt.Stringer); ok {
 		bodyReader = strings.NewReader(val.String())
 	}

It turns out that by wrapping the concrete type in an empty interface, we can then check if it is an interface type πŸš€

Written by Jamie Tanna's profile image Jamie Tanna on , and last updated on .

Content for this article is shared under the terms of the Creative Commons Attribution Non Commercial Share Alike 4.0 International, and code is shared under the Apache License 2.0.

#blogumentation #go.

πŸ€– Content in this blog post (prose or code snippets) includes code derived from the following LLMs:

  • claude:sonnet-3.7-thinking

This post was filed under articles.

Interactions with this post

Interactions with this post

Below you can find the interactions that this page has had using WebMention.

Have you written a response to this post? Let me know the URL:

Do you not have a website set up with WebMention capabilities? You can use Comment Parade.