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
- Most Common Golang Errors and How to Fix Them
- Go: Convert String to Int and Int to String
- Go: Convert Rune to String and String to Rune
- Fix 'nil pointer dereference' in Go
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.