Explicit Conversion Syntax
Understand Go's T(v) explicit conversion syntax, why Go demands explicit type conversions, and how to use it correctly with numbers, strings, and custom types.
Go treats types as a central part of its design. One of the most visible consequences is that the language never silently changes a value's type behind your back. The syntax you use to tell the compiler "I want this value to be treated as a different type" is T(v). This page is about that exact syntax: what it is, how it works, and how to think about it as you write Go.
The T(v) Conversion Form
In Go, converting a value v to type T is written as:
T(v)
T is any valid type name, and v is an expression whose value should be converted. The parentheses around v are mandatory. If you come from C, Java, or Python, this might look like a function call or a constructor, but it is not a function — it is a conversion expression built directly into the language.
Go has no typecasting keyword:
The Go specification never uses the word "typecasting." If you search the spec for "cast", you will not find it. What other languages call typecasting, Go calls type conversion, and the conversion syntax is T(v). The terms are often used interchangeably in day-to-day conversation, but in Go documentation, always look for conversion.
Here is the simplest example that shows the syntax in action:
var i int = 42
var f float64 = float64(i) // T = float64, v = i
The integer i does not become a float automatically; you have to write float64(i). Remove that explicit conversion and the compiler will refuse to compile the assignment.
Go's insistence on explicit conversions is not a quirk — it is a deliberate safety feature. When a conversion can lose information (like turning a float64 into an int), the code that causes the loss is visible and has to be written out. There is no hidden truncation or silent wrapping.
Why the syntax looks the way it does
In many languages, conversions use syntax like (int) x or int(x). Go chose T(v) instead of (T)v for a practical reason: the parenthesized form (T)v would conflict with parsing in Go's grammar, and T(v) aligns with the language's general preference for clean, function-like syntax without excess punctuation. For a beginner, it is enough to remember: type name, then parentheses with the value inside.
What counts as a valid conversion
You cannot write T(v) for any random pair of types. The Go spec defines a short list of cases where a non‑constant value v can be converted to type T. The most important ones for everyday Go are:
- Between numeric types: You can convert any integer or floating‑point type to any other numeric type (e.g.
int→float64,uint16→int32,float32→int). The next section in this chapter covers the details of what happens to the value during numeric conversions. - Between a string and a byte or rune slice:
string([]byte{'h','i'})gives"hi".[]byte("hi")gives a byte slice. The same works for[]rune. - Between types with the same underlying type: If you define
type Age int, you can convert aninttoAgewithAge(42)and anAgeback tointwithint(someAge). - From a concrete type to an interface type that the concrete type implements. That is not a direct
T(v)conversion; it happens implicitly in assignments and will be explored in the interfaces chapter.
Invalid conversions stop at compile time:
Trying to convert an int to a bool or a struct to a string with bool(1) or string(myStruct) will produce a compiler error. There is no magic — the compiler checks that the conversion is one of the allowed cases and rejects everything else.
Conversions between types with identical underlying types deserve a closer look because they confuse many newcomers. In Go, type Age int creates a new named type whose underlying type is int. Even though Age and int share the same internal representation, the compiler treats them as different types. You cannot assign an Age to an int variable directly, but you can convert between them explicitly:
type Age int
var a Age = 25
var i int = int(a) // Explicit conversion required
var b Age = Age(30) // Converting an int literal to Age
Without the conversion, the assignment var i int = a would fail with a type mismatch error. This strictness keeps code explicit, especially in large codebases where custom types carry semantic meaning beyond their raw representation.
The syntax in practice
The syntax is identical regardless of whether you are converting a variable, a literal, or the result of a function call:
x := 10
y := float64(x) // variable
z := float64(3 + 4) // expression
w := uint(math.Sqrt(25.0)) // return value, with two conversions
Each conversion is independent; you can chain them if needed, but the intermediate types must make sense. In the last line, math.Sqrt returns a float64, which is converted to uint. That conversion truncates the fractional part, so w becomes 5.
Notice the chained conversion: you first have to convert the result of Sqrt (a float64) to uint. The compiler does not allow uint(math.Sqrt(...)) to skip a step if it is valid — and here it is valid because a float64 can be directly converted to uint. But generally, each step must be a valid conversion on its own.
Conversions can lose information silently:
Even though the syntax is explicit, the compiler does not warn you if a conversion loses data. int(3.14) silently truncates to 3. The burden is on you to ensure the result is what you expect. Later sections on numeric conversions will discuss overflow, underflow, and precision loss in detail.
Converting between strings and byte slices
One of the most common uses of T(v) outside of numbers is moving between string and []byte. The conversion creates a new copy of the underlying data, which is important to remember for performance‑sensitive code.
s := "hello"
b := []byte(s) // a new byte slice containing the UTF‑8 bytes of "hello"
s2 := string(b) // back to a string
This works similarly for []rune if you need to work with Unicode code points individually.
Common pitfalls for beginners
Beginners often expect int("42") to give them the integer 42. It does not. It gives the integer value of the first byte of the string when interpreted as a rune. In practice, that means int("4") is 52 (the ASCII code for '4'), not 4. To parse numbers out of strings, you need the strconv package, not a conversion expression.
int(string) is not string-to-number parsing:
Do not confuse int(x) where x is a string with parsing. int("1") yields 49. Use strconv.Atoi("1") to get the integer 1. This is a top‑3 mistake for new Gophers.
Another common point of confusion is that a conversion expression does not change the original value — it produces a new value of the target type. The original variable remains untouched.
var num int = 9
var fl float64 = float64(num) // fl is 9.0, num is still 9
This is obvious once you think about it, but when you are new to the language, it is easy to momentarily picture float64(num) as an "in‑place" change.
When to use explicit conversion
You will reach for T(v) whenever Go's strong typing prevents you from mixing types in an operation or assignment. The most frequent situations are:
- Arithmetic between mixed numeric types — Go does not let you add an
intand afloat64without a conversion. - Assigning a concrete value to a variable of a custom defined type that shares the same underlying structure.
- Passing arguments to a function that expects a specific type.
- Converting between string and byte/rune slices for I/O or encoding work.
In all these cases, the syntax is the same: T(v). There is no separate "casting" operator or keyword to learn.
Thinking about conversions as a beginner
If you have never worked in a language that demands explicit conversions, this rule can feel like extra typing. Over time, you will notice that it makes bugs easier to spot. When you see int(f) in a code review, you immediately know a truncation is happening. That clarity is the whole point.
A helpful mental model: treat T(v) as a statement of intent. It says, "I know v is not a T right now, but I am choosing to reinterpret it as a T, and I accept whatever consequences come with that choice." Writing that explicitly changes how you read and debug code.
You’re on the right track:
If your IDE or terminal shows a type mismatch error and your first instinct is to wrap the value with targetType(…), you are already thinking like a Go programmer. That is the expected workflow — not a workaround.
Summary
The conversion syntax T(v) is the only mechanism Go provides for changing a value's type. It is intentionally simple and intentionally explicit. Every conversion must be written out, making potential data loss or surprising behavior visible in the source code. The syntax covers numeric conversions, string‑byte‑slice conversions, and conversions between types that share an underlying type. It does not parse strings into numbers, and it does not silently bridge unrelated types.