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.

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.