Represent and transform data safely

Go Interface to String: When to Assert and When to Format

Need a string from a Go interface? Learn when to use a checked type assertion, fmt.Sprint, fmt.Sprintf, or fmt.Stringer.

By Niko Minadze3 min read

Editorial illustration for Go Interface to String: When to Assert and When to Format

If an interface value holds a string and you need that string, use s, ok := value.(string). If you need text describing whatever the interface holds, use fmt.Sprint(value) or fmt.Sprintf. The distinction matters: formatting an integer as "42" does not make the integer inside the interface a string.

Retrieve a stored string with a type assertion

An interface{} variable can hold values of different dynamic types. A string assertion asks one specific question: is the value currently held by this interface a string? The comma-ok form gives you a result and a boolean, so a mismatch can be handled without a panic. By contrast, value.(string) without ok panics when the dynamic type is not string.

Here is a complete example. It first retrieves a stored string, then reuses the same interface variable for values that need different treatment:

package main

import "fmt"

type Phase int

func (Phase) String() string {
    return "queued"
}

func main() {
    var value interface{} = "ready"

    if s, ok := value.(string); ok {
        fmt.Println("stored:", s)
    }

    value = 42
    if s, ok := value.(string); ok {
        fmt.Println("stored:", s)
    } else {
        fmt.Println("not a string")
    }

    fmt.Println(fmt.Sprint(value))
    fmt.Println(fmt.Sprintf("value: %v", value))

    value = []int{1, 2, 3}
    fmt.Println(fmt.Sprint(value))
    fmt.Println(fmt.Sprintf("value: %v", value))

    value = Phase(1)
    fmt.Println(fmt.Sprint(value))
    if stringer, ok := value.(fmt.Stringer); ok {
        fmt.Println(stringer.String())
    }
}

The first check finds the stored "ready". After value = 42, the second check takes the else branch: the interface now holds an int, not a string. The ok check is useful precisely when the value's dynamic type is not already guaranteed at the point of use. If your next operation requires an actual stored string, formatting the integer is not a substitute for deciding what to do with the mismatch.

Format a representation of any value

For the integer in the example, fmt.Sprint(value) produces "42", while fmt.Sprintf("value: %v", value) produces "value: 42". For the slice, the corresponding results are "[1 2 3]" and "value: [1 2 3]". Those are strings returned by the formatting calls; the interface still holds an integer or a slice until it is assigned something else. Examples of these default representations and prefixed %v output show why formatting is handy when the dynamic type may vary.

Use fmt.Sprint when the default representation alone is enough. Use fmt.Sprintf when you need to place that representation into a larger message, as in "value: %v". For a single value, fmt.Sprint(value) and fmt.Sprintf("%v", value) produce the same default representation. Neither expression checks whether value contains a string; both ask fmt to render the value they receive.

A String() method affects formatting, not the stored type

The example's named Phase type has a String() string method. That is the method required by fmt.Stringer. When value holds a Phase, fmt.Sprint(value) uses the method and returns "queued". It does not change the dynamic type from Phase to string, so value.(string) would still be the wrong assertion.

The final if makes a different assertion: it checks whether the value implements fmt.Stringer before calling String() directly. That can be useful when you specifically want the type's method. It is not a way to call String() on every interface value—an integer or slice in this example does not acquire that method just because fmt.Sprint can format it.

The choice is therefore about what your next step requires. Assert string when it needs the actual string held in the interface, and handle ok == false. Format when it needs a textual representation of the value, whatever its dynamic type may be.

Comments

No comments yet.

Leave a comment

Your comment appears after approval. Plain text only; links stay as text. Line breaks and indentation are kept.

Up to 5,000 characters. No account or email needed.

All articles

Type at least two characters.

Press <kbd>Esc</kbd> to closeOpen the search page