}

Fix 'cannot use X (type Y) as type Z' in Go — Type Mismatch Guide

cannot use x (type Y) as type Z

The immediate fix: Go never converts types implicitly. Add an explicit conversion — Z(x) — to tell the compiler you intend the conversion. For example, int(myFloat64) or string(myByteSlice).

Before (broken):

var f float64 = 3.14
var i int = f // cannot use f (type float64) as type int

After (fixed):

var f float64 = 3.14
var i int = int(f) // explicit conversion: truncates decimal part

If the conversion is not that simple — interfaces, pointers, custom types — read on for each case.


Why This Happens

Go has a strict, static type system with no implicit type coercion. In languages like JavaScript or Python, many conversions happen silently. In Go, the compiler refuses every conversion that is not explicit. This design catches entire categories of bugs — accidentally losing precision, comparing incompatible types, or passing the wrong struct to a function — before the program ever runs.

The error message cannot use X (type Y) as type Z tells you exactly what happened: you have a value of type Y in a context that requires type Z, and no implicit conversion exists between them.


Case 1: Numeric Type Conversions

Go treats int, int8, int16, int32, int64, uint, float32, and float64 as completely separate types. Even int and int64 are different on 64-bit systems.

Broken:

package main

import "fmt"

func add(a, b int64) int64 {
    return a + b
}

func main() {
    var x int = 10
    var y int = 20
    result := add(x, y) // cannot use x (type int) as type int64
    fmt.Println(result)
}

Fixed:

func main() {
    var x int = 10
    var y int = 20
    result := add(int64(x), int64(y)) // explicit conversion
    fmt.Println(result)
}

Watch out for precision loss:

var f float64 = 3.99
var i int = int(f) // i == 3, not 4 — truncates toward zero, no rounding

String and byte slice conversions are also explicit:

s := "hello"
b := []byte(s)  // string → []byte
s2 := string(b) // []byte → string

Case 2: Named Type vs Underlying Type

Even if two types have the same underlying type, Go treats them as distinct.

Broken:

package main

import "fmt"

type Celsius float64
type Fahrenheit float64

func toCelsius(f Fahrenheit) Celsius {
    return (f - 32) * 5 / 9
}

func main() {
    boiling := Fahrenheit(212)
    c := toCelsius(boiling)

    var rawTemp float64 = 100.0
    // cannot use rawTemp (type float64) as type Fahrenheit
    c2 := toCelsius(rawTemp)
    fmt.Println(c, c2)
}

Fixed:

func main() {
    boiling := Fahrenheit(212)
    c := toCelsius(boiling)

    var rawTemp float64 = 100.0
    c2 := toCelsius(Fahrenheit(rawTemp)) // convert to named type first
    fmt.Println(c, c2)
}

Named types are a powerful feature: they let you define methods on the type and catch errors where, say, Celsius values are accidentally passed where Fahrenheit is expected.


Case 3: Interface Satisfaction Errors

When a type does not implement all methods of an interface, you get a related error:

cannot use myStruct (type MyStruct) as type io.Writer in argument to write:
MyStruct does not implement io.Writer (missing Write method)

Broken:

package main

import (
    "fmt"
    "io"
)

type MyWriter struct{}

// Write method has wrong signature — returns no values
func (w MyWriter) Write(data []byte) {
    fmt.Println(string(data))
}

func writeAll(w io.Writer, data []byte) {
    w.Write(data)
}

func main() {
    writeAll(MyWriter{}, []byte("hello"))
    // cannot use MyWriter literal (type MyWriter) as type io.Writer:
    // MyWriter does not implement io.Writer (wrong type for Write method)
}

Fixed — match the interface signature exactly:

// io.Writer requires: Write(p []byte) (n int, err error)
func (w MyWriter) Write(data []byte) (int, error) {
    fmt.Println(string(data))
    return len(data), nil
}

Compile-time interface check pattern — add this to catch the mismatch at the type definition, not at the call site:

var _ io.Writer = MyWriter{} // compile error here if MyWriter doesn't satisfy io.Writer

Case 4: Pointer vs Value Receiver Mismatch

If a method is defined on *T (pointer receiver), you cannot pass a T value where *T is expected, and vice versa.

Broken:

package main

import "fmt"

type Counter struct {
    count int
}

// pointer receiver — modifies the original
func (c *Counter) Increment() {
    c.count++
}

type Incrementer interface {
    Increment()
}

func run(i Incrementer) {
    i.Increment()
}

func main() {
    c := Counter{}      // value, not pointer
    run(c)              // cannot use c (type Counter) as type Incrementer:
                        // Counter does not implement Incrementer
                        // (Increment method has pointer receiver)
}

Fixed:

func main() {
    c := &Counter{}     // pointer
    run(c)              // *Counter implements Incrementer
    fmt.Println(c.count) // 1
}

The rule: - Methods on *T are NOT in the method set of T (value). - Methods on *T ARE in the method set of *T (pointer). - Methods on T are in the method sets of BOTH T and *T.

So if any method in an interface has a pointer receiver, you must pass a pointer to satisfy that interface.


Case 5: Type Assertions with the ok Pattern

Type assertions (x.(T)) extract a concrete type from an interface. Without the two-value form, a failed assertion panics at runtime.

Broken — single-value assertion, panics if wrong type:

package main

import "fmt"

func printLength(v interface{}) {
    s := v.(string) // PANICS if v is not a string
    fmt.Println(len(s))
}

func main() {
    printLength("hello") // fine
    printLength(42)      // panic: interface conversion: interface {} is int, not string
}

Fixed — two-value (ok) assertion:

func printLength(v interface{}) {
    s, ok := v.(string)
    if !ok {
        fmt.Printf("expected string, got %T\n", v)
        return
    }
    fmt.Println(len(s))
}

Type switch for handling multiple types cleanly:

func describe(v interface{}) {
    switch val := v.(type) {
    case string:
        fmt.Printf("string of length %d\n", len(val))
    case int:
        fmt.Printf("integer: %d\n", val)
    case []byte:
        fmt.Printf("byte slice of length %d\n", len(val))
    default:
        fmt.Printf("unknown type: %T\n", val)
    }
}

Case 6: Struct Literal Type Mismatch

Two struct types with identical fields are still different types in Go unless one is an alias of the other.

Broken:

package main

import "fmt"

type PointA struct{ X, Y int }
type PointB struct{ X, Y int }

func move(p PointA) PointA {
    return PointA{p.X + 1, p.Y + 1}
}

func main() {
    b := PointB{X: 1, Y: 2}
    move(b) // cannot use b (type PointB) as type PointA
    fmt.Println(b)
}

Fixed:

func main() {
    b := PointB{X: 1, Y: 2}
    a := PointA(b) // explicit conversion works when fields are identical
    result := move(a)
    fmt.Println(result)
}

Note: explicit struct conversion only works when both structs have the same field names, types, and order, and neither has unexported fields from a different package.


Quick Diagnosis Table

Error pattern Root cause Fix
cannot use x (type int) as type int64 Numeric type mismatch int64(x)
cannot use x (type MyType) as type float64 Named vs underlying type float64(x)
does not implement interface (missing method) Method not defined Add the missing method
does not implement interface (wrong type for method) Method signature mismatch Fix the method signature
Increment method has pointer receiver Pointer vs value receiver Pass &myStruct
interface conversion ... is not string Failed type assertion Use two-value form x, ok := v.(string)

Related Guides


FAQ

Can Go ever convert types implicitly?

Almost never. The only implicit conversions are: untyped constants are assigned the type required by context, and interface values are implicitly assigned when the underlying type satisfies the interface. Everything else — numeric types, named types, structs — requires an explicit conversion.

What is the difference between a type conversion and a type assertion?

A type conversion (T(x)) changes the type of a value at compile time and is checked statically. A type assertion (x.(T)) extracts a concrete type from an interface at runtime and can fail. Type conversions are between concrete types; type assertions are from interface to concrete type.

When should I use interface{} (or any) vs a concrete type?

Use concrete types whenever possible — they are faster, safer, and provide better IDE support. Use interface{} / any only when you genuinely need to handle values of unknown type (e.g., JSON unmarshalling into unknown structures, generic utility functions that predate Go generics). With Go 1.18+, prefer generic functions (func Foo[T any](v T)) over interface{} for type-safe polymorphism.