Skip to content
gohacks.dev
Basics

Cannot Use Value as Type in Assignment Error in Go

This compile error means the types differ and Go will not convert for you. Read its seven shapes, from int32 and int64 to a missing interface method.

6 min read
Summarize in AI
Flat illustration of a log with a failing line and a wrench.

This is the compile error with the most variations, because it covers every place a value is put where a different type is expected: assignments, declarations, function arguments, returns, struct fields. The wording is consistent, and the useful part is learning to read what it says. It names the value, its type, the type it was put into and sometimes a reason after a colon. This article goes through the seven shapes you will meet, shows the compiler's actual output for each, and gives the fix, with the places where the fix is not a conversion but a design decision. It follows type conversion in Go, which explains the rules behind the fixes, and pointer or value receiver for the interface case. It is part of the Go basics path.

Tested with Go 1.25.6, macOS on Apple Silicon (arm64)

The seven shapes

One file with seven mistakes, and the compiler's response to each:

main.goGo
package main

import (
	"fmt"
	"time"
)

type Celsius float64

type Shape interface{ Area() float64 }

type Square struct{ side float64 }

func (s *Square) Area() float64 { return s.side * s.side }

func main() {
	var small int32
	var big int64 = 10
	small = big

	var name string = []byte("ada")

	var c Celsius = 20.5
	var f float64 = c

	var timeout time.Duration
	seconds := 5
	timeout = seconds

	var s Shape = Square{2}

	var p *int = 5

	var label string = nil

	fmt.Println(small, name, f, timeout, s, p, label)
}
Text
# example.test/lab
./main.go:19:10: cannot use big (variable of type int64) as int32 value in assignment
./main.go:21:20: cannot use []byte("ada") (value of type []byte) as string value in variable declaration
./main.go:24:18: cannot use c (variable of float64 type Celsius) as float64 value in variable declaration
./main.go:28:12: cannot use seconds (variable of type int) as time.Duration value in assignment
./main.go:30:16: cannot use Square{…} (value of struct type Square) as Shape value in variable declaration: Square does not implement Shape (method Area has pointer receiver)
./main.go:32:15: cannot use 5 (untyped int constant) as *int value in variable declaration
./main.go:34:21: cannot use nil as string value in variable declaration

Read each line of the output the same way: the part before "as" is what you gave, and the part after is what the destination wanted. The format is always cannot use <expression> (<kind> of type <T>) as <U> value in <context>, where the context says assignment, variable declaration, argument to f, return statement or struct literal. When the message has more after a colon, that is the compiler explaining why the types are not compatible.

Fix for each

Different integer sizes (int64 as int32). Go has no implicit widening or narrowing. Convert explicitly, small = int32(big), and check the range first if the value is not bounded, because narrowing wraps silently (see integer overflow). If you find yourself converting back and forth, one of the declarations probably has the wrong type for the data.

[]byte as string. The two are different types with a conversion between them: string(b) and []byte(s). Each copies the data, so doing it in a loop adds allocations, as strings as bytes measures. The cheapest fix is to choose one representation per pipeline stage and convert once.

A named type as its underlying type (Celsius as float64). A defined type is a new type even though its representation is identical. That is the point of defining one: the compiler stops you passing a temperature where a distance is expected. Convert explicitly at the boundary, float64(c), and keep the named type inside your own code.

int as time.Duration. A Duration is an int64 count of nanoseconds, and an int variable is not one. Convert, and multiply by the unit, time.Duration(seconds) * time.Second. A bare time.Duration(seconds) would compile and mean seconds nanoseconds, which is the bug to avoid.

A struct value as an interface it does not satisfy. The compiler's explanation after the colon, Square does not implement Shape (method Area has pointer receiver), is the actual diagnosis. Area is defined on *Square, so only a *Square satisfies Shape. Fix it by passing &Square{2} or by changing the receiver to a value if the type is small and immutable. The receiver choice covers which to choose. The other form of this message says a method is missing: missing method Area, and the fix is to add the method or use a different type.

An untyped constant as a pointer (5 as *int). A pointer is not a number. To get a pointer to a value, assign the value to a variable and take its address, n := 5; p := &n, or write a small generic helper for it. There is no &5.

nil as a non-nillable type. string, numbers, booleans and structs have no nil. Use the zero value ("", 0, false), or a pointer type if "absent" must be representable, as structs and optional fields shows.

Why untyped constants slip through

The reason var d time.Duration = 5 compiles while d = seconds does not is that a constant like 5 has no type until it is used. It takes the type of its destination if it fits, so 5 becomes a Duration of five nanoseconds. A variable has a fixed type, so it needs a conversion. If you see the error and the right-hand side looks like a plain number, check whether it came from a variable. The behavior is described in typed and untyped constants, and it is the same mechanism that lets x * 2 work for a float x.

Reading the compiler's output well

Three habits make these messages quick to fix. Look at the types in the message first, since they are exactly the two things that disagree. Look at the context word, assignment or argument to, which tells you whether the problem is at the call site or in the callee's signature. And when the destination is an interface, read to the end of the line, because the reason is there and it is the whole answer. If a conversion looks too long to be right, step back and check whether the declared type is the mistake: a function that takes int and is called with int64 from everywhere probably wants int64, and changing one signature beats converting at fifty call sites.

Version and tooling notes

The shape of the message has been stable for years, though older compilers word the middle part slightly differently, with (type A) where Go 1.25 writes (variable of type A), so match on the structure and not the exact text. Editors show the same error inline from gopls, and go vet has nothing to add, since this is a type error and not a style one. With generics, a related error, T does not satisfy constraint, says the same kind of thing about type arguments, and the idea of reading both types is identical.

When the conversion is not the answer

If you reach for T(x) and the compiler still says no, the types are not convertible: a struct to a string, a map to a slice, an error to a concrete type. Those need real code, a method that formats the struct, a loop, or a type assertion for the interface case, as in type assertions and type switches. If you convert in many places because two packages model the same concept with different types, the better fix is to agree on one type, or to isolate the translation in a single function at the boundary. And if the conversion would lose information, such as a float to an int or a large int to a small one, decide on the rounding or range behavior on purpose. The specification on assignability lists every case where Go will accept a value, and the type conversion article on go.dev's tour shows the explicit syntax in a runnable page.

Taggederror fix
Share

Keep reading

Get new articles by email

We send one email when a new article is published. No spam.

Unsubscribe in one click. No spam, ever.