Fix

"cannot use X as type Y in assignment"

Fix the Go type mismatch error by explicitly converting the source value to the target type using a type assertion or conversion.

Fix: "cannot use X as type Y in assignment"

You're building a price calculator. You have a quantity as an int and a unit price as a float64. You multiply them and try to store the total in a variable declared as int. The compiler stops you immediately with cannot use total (untyped float constant) as int value in assignment. Or you're working with interfaces. You have a fmt.Stringer and try to shove it into a string variable without asking nicely. Go says no. This error is Go's way of enforcing type safety. It won't guess what you mean. You have to tell it exactly how to transform the data. The error message names the value and the expected type. It tells you precisely where the mismatch occurs.

Types and conversions in plain words

Go checks types at compile time. Every variable has a specific type, and that type defines what operations are allowed. If you try to assign a value of one type to a variable of a different type, the compiler rejects it unless the types are identical or there is an explicit conversion. Think of types as strict shipping containers. An int container holds whole numbers. A float64 container holds decimals. You can't just dump the contents of one container into the other without a transfer mechanism. The compiler forces you to declare the transfer. This prevents silent data loss. If you convert a float64 to an int, you lose the decimal part. Go wants you to acknowledge that loss. Type safety also prevents accidental misuse. You can't pass a user ID where a database connection is expected. The compiler catches the mistake before the code runs.

Go supports very few implicit conversions. Constants can be untyped until assigned, but variables are always typed. Once a value is typed, it stays that type. The compiler rejects the assignment and emits an error message naming the source type and the target type. You fix the code by adding a conversion expression. The conversion expression takes the form TargetType(value). The compiler verifies that the conversion is valid between the two types. If the types are incompatible, like converting a string to an int, the compiler rejects the conversion with cannot convert value (type string) to type int.

Go won't guess. You declare the transformation.

Minimal example

package main

import "fmt"

// Main demonstrates type mismatch and conversion.
func main() {
	// count is an int.
	count := 10

	// rate is a float64.
	rate := 2.5

	// total needs to hold the result of count * rate.
	// The product of int and float64 is float64.
	// Assigning float64 to int causes a compile error.
	// Uncommenting the next line triggers:
	// cannot use count * rate (untyped float constant) as int value in assignment
	// var total int = count * rate

	// Convert the result to int explicitly.
	// This truncates the decimal part.
	var total int = int(count * rate)

	fmt.Println(total) // Output: 25
}

How the compiler handles this

When the compiler encounters an assignment, it looks at the type of the expression on the right and the type of the variable on the left. If they match exactly, the assignment proceeds. If they differ, the compiler checks for an implicit conversion. Constants are untyped until used. The literal 3.14 is an untyped float constant. You can assign it to a float32 or a float64 variable. The compiler chooses the target type and converts the constant. However, once a value is stored in a variable, it has a fixed type. Variables are always typed. You cannot assign a float32 variable to a float64 variable without conversion. The compiler rejects the assignment with cannot use value (type float32) as float64 value in assignment. The error message distinguishes between untyped constants and typed values. If the error mentions untyped, you are dealing with a constant. If it mentions a type, you are dealing with a variable or expression result.

The compiler also checks conversion validity. You can convert between numeric types, but not between unrelated types. Converting a string to an int is invalid. The compiler rejects this with cannot convert value (type string) to type int. You must use a parsing function like strconv.Atoi instead.

Realistic examples

Real code often involves interfaces and JSON. When you unmarshal JSON, you get map[string]interface{}. You need to extract values. You can't just assign an interface{} to a string. You must assert the type.

package main

import (
	"encoding/json"
	"fmt"
)

// ExtractName retrieves the name from a JSON map.
// It demonstrates type assertion from interface{} to string.
func ExtractName(data map[string]interface{}) (string, error) {
	// rawName holds the value from the map.
	// The map value type is interface{}.
	rawName, ok := data["name"]

	// Check if the key exists.
	if !ok {
		return "", fmt.Errorf("missing name field")
	}

	// rawName is interface{}, not string.
	// Assigning rawName to a string variable fails.
	// Type assertion converts interface{} to the concrete type.
	// The comma-ok idiom checks if the assertion succeeds.
	name, ok := rawName.(string)
	if !ok {
		return "", fmt.Errorf("name is not a string, got %T", rawName)
	}

	return name, nil
}

func main() {
	jsonBytes := []byte(`{"name": "Alice", "age": 30}`)
	var payload map[string]interface{}

	// Unmarshal populates the map with interface{} values.
	if err := json.Unmarshal(jsonBytes, &payload); err != nil {
		panic(err)
	}

	name, err := ExtractName(payload)
	if err != nil {
		panic(err)
	}

	fmt.Println(name)
}

Database drivers often return int64. Your struct uses int. This mismatch is common.

package main

// User represents a database record.
type User struct {
	ID   int    // Application uses int for IDs.
	Name string
}

// FetchUser simulates a database query.
// Database drivers often return int64 for integer columns.
func FetchUser() (User, error) {
	// dbID comes from the driver as int64.
	var dbID int64 = 101

	// name comes from the driver as string.
	var name string = "Alice"

	// Assigning dbID to u.ID fails.
	// int and int64 are distinct types.
	// The compiler rejects this with:
	// cannot use dbID (variable of type int64) as int value in struct literal
	// u := User{ID: dbID, Name: name}

	// Convert dbID to int explicitly.
	// This assumes the value fits in an int.
	u := User{ID: int(dbID), Name: name}
	return u, nil
}

Interface assertions are runtime checks. Verify before you cast.

Pitfalls and common errors

Go has distinct integer types. int is the native word size of the machine. int64 is a fixed 64-bit integer. They are not interchangeable. On a 64-bit system, int is 64 bits, but it is still a different type from int64. Assigning an int64 to an int triggers the error. Convert explicitly with int(value). Be aware that converting a large int64 to int on a 32-bit system may overflow. The compiler does not check for overflow during conversion.

Defining a new type creates a distinct type. type ID int. ID is not int. var id ID = 42 works. var x int = id fails. Convert with int(id). This pattern prevents mixing values of the same underlying type but different semantic meaning. You can't accidentally pass a user ID where a product ID is expected. A type alias creates a synonym. type ID = int. ID and int are the same type. No conversion needed. Use aliases for readability, not for type safety. A type definition creates a new type. Use definitions when you need to attach methods or prevent accidental mixing.

Converting an integer to a string requires the strconv package. The expression string(42) compiles, but it converts the integer to the corresponding Unicode code point, resulting in the character "*". To get the decimal string "42", use strconv.Itoa(42). The compiler won't stop you from using string() on an int, so this bug slips through to runtime.

Interface assertions can panic. If you use value.(Type) without the comma-ok idiom and the value doesn't hold that type, the program panics with interface conversion: interface{} is nil, not string. Always use the comma-ok form in untrusted code.

Go functions return errors as the last value. Check them immediately. if err != nil { return err } is the standard pattern. It makes the failure path visible. Don't hide errors behind silent conversions. If a conversion might fail, like parsing a string, use a function that returns an error and handle it. When defining methods on a type, use a short receiver name. (id ID) String() string. Not (this ID). The receiver name is usually one or two letters matching the type.

The compiler catches type mismatches. You catch logic errors.

When to use conversions and assertions

Use a type conversion T(v) when you need to change a numeric type to another numeric type, like int to float64, or when converting a byte to a rune. The conversion changes the representation but keeps the value semantics.

Use a type assertion v.(T) when you have an interface value and need to extract the underlying concrete type. The assertion checks the dynamic type at runtime.

Use the comma-ok idiom v, ok := v.(T) when the interface value might not hold the target type. This prevents panics and lets you handle the mismatch gracefully.

Use strconv functions when converting between numbers and strings. strconv.Itoa converts an int to a decimal string. strconv.Atoi parses a string to an int.

Use fmt.Sprintf when you need formatted string conversion with placeholders. It handles multiple types and formatting rules in one call.

Use a custom method or function when the conversion involves complex logic that built-in conversions can't express. Encapsulate the logic in a named function for clarity.

Use explicit conversion between int and int64 when crossing boundaries between application code and libraries that use fixed-width integers. Database drivers and serialization formats often use int64.

Pick the tool that matches the transformation. Conversions for numbers, assertions for interfaces, parsing for strings.

Where to go next