In Go, what is the difference between type Celsius float64 and type Celsius = float64?
answer
- one character apart, worlds apart
- does it create a type or a name?
- only one of the two can carry methods
- an alias is identical, not merely similar
basics
~20 stype Celsius float64 declares a new, distinct type that shares float64's representation but has its own method set. type Celsius = float64 is an alias: Celsius and float64 are two names for one identical type.
solid answer
~50 s`type Celsius float64` is a *type definition*. It creates a brand-new named type whose underlying type is `float64`, so it has the same representation and supports the same arithmetic, but the compiler treats it as its own type: a `Celsius` is not interchangeable with a `float64` unless you say so explicitly. Because the type is declared in your package, you can hang methods on it, such as `func (c Celsius) String() string`. That is how a units library turns mixing quantities into a compile error. `type Celsius = float64` is a *type alias*. It introduces no new type at all, only a second spelling: `Celsius` and `float64` are identical everywhere, in signatures, in interfaces, in `reflect` and in `%T`. Nothing ever needs converting, and you cannot declare a method on it, because the receiver type would be `float64`, which your package does not declare. Aliases exist mainly to keep an old name working while a type moves between packages.
code
go · 9 linestype Celsius float64 // definition: new type, its own method set
type Temp = float64 // alias: just another spelling of float64
func (c Celsius) String() string {
return fmt.Sprintf("%.1f\u00b0C", float64(c))
}
// func (t Temp) String() string { ... }
// compile error: the receiver type is float64, which this package does not declarego deeper
Be ready to state the difference in one sentence and to spot the equals sign in a declaration. Know that only the defined type can have methods declared on it.
Explain what underlying type means, why a definition starts with an empty method set, and why Go forbids declaring a method whose receiver type is declared in another package.
Show judgment about when a distinct type earns its keep in a real API, and be able to say what silently breaks when a definition in a shipped package is changed into an alias.
Own the API policy: which domain distinctions the compiler should enforce across teams, and the rule that an exported alias is a temporary migration artefact with a named owner and a removal date.
## Two declarations that differ by one character Go's `type` keyword introduces two quite different things, and the only visible difference is an `=`. ### The type definition: `type Celsius float64` This creates a **new named type**. Its **underlying type** is `float64`, and the underlying type is what decides how values are represented and which operators work: a `Celsius` is an IEEE-754 double, you can add, subtract, multiply and compare two `Celsius` values, and it occupies the same eight bytes a `float64` does. What it is *not* is `float64`. The compiler tracks `Celsius` as its own type with its own identity. A function that takes a `float64` will not silently swallow a `Celsius`; you have to write the conversion `float64(c)` yourself, which is the whole point — the compiler now has a name for the distinction you care about. A definition also gives you something you cannot otherwise have: **a place to declare methods**. Go allows a method only in the package that declares the receiver's type, so you can never attach behaviour to `float64`, `[]string` or `map[string]int` directly. Defining `type Celsius float64` in your package creates a type you *do* own, and `func (c Celsius) String() string` becomes legal. Once it has methods it can also satisfy interfaces (here, `fmt.Stringer`) that the underlying type never could. Note what a definition does **not** copy: the methods declared on the source type. `type Celsius float64` starts life with an empty method set — there is nothing on `float64` to lose, but the same rule bites when you define a type from a struct that has methods. A defined type costs nothing at run time. It is a compile-time distinction; there is no wrapper, no boxing, no allocation. The only price is the conversions you write in the source. ### The type alias: `type Celsius = float64` This creates **no type at all**. It binds a second name to an existing type. Afterwards `Celsius` and `float64` are the same type in every way the language cares about: - assignment in both directions needs no conversion; - they have the same method set, so they satisfy exactly the same interfaces; - `reflect` reports the target type, and `fmt`'s `%T` prints the target's name; - a type switch cannot distinguish them, because there is nothing to distinguish; - you cannot declare a method on the alias name, because the receiver type resolves to a type your package did not declare. An alias is a naming device, not a typing device. That is exactly why it is useful for **migration**: when a type moves from one package to another, leaving `type Old = newpkg.T` behind means old and new names denote one type, so code that has already migrated and code that has not can pass values to each other freely. An alias is also handy as a short local name for a long instantiated generic type, and since Go 1.24 an alias may declare type parameters of its own. ### Choosing between them Ask what you want the compiler to know. - **You want a new distinction the compiler enforces** — units, ids, states, a slice or map you intend to give methods: define a type. - **You want the same type under a different name** — a compatibility shim during a move, a shorthand: write an alias. A useful review habit: an alias in a package's exported API is a temporary artefact and should say in a comment why it exists and when it goes away. A defined type is permanent API. ### Reading it in a diff The `=` is easy to miss, and the two forms fail in opposite directions. Turning a definition into an alias silently deletes a type distinction the codebase depended on; turning an alias into a definition breaks every caller at once with conversion errors. When you are unsure which one a package uses, `go doc` prints the declaration verbatim — an alias shows up as `type Celsius = float64` — and go-to-definition in an editor jumps straight through an alias to the type it names.
- Can you declare a method on an alias such as type Duration = time.Duration?No. The receiver type resolves to `time.Duration`, and Go only lets you declare a method in the package that declares the receiver's type. The alias adds no new type to hang a method on. If you need methods, use a definition — `type Duration time.Duration` — but be aware it starts with an empty method set of its own.
- Does type Celsius float64 cost anything at run time?Nothing. A defined type is a compile-time construct: a `Celsius` has exactly the representation of a `float64`, with no wrapper, no boxing and no extra allocation. The distinction disappears after type checking, so domain types like this are free — the only cost is writing the conversions.
- If a struct field is declared with an alias type, does anything about the struct change?No. The alias denotes the same type, so field layout, size, composite literals, struct tags, encoding and reflection all behave exactly as if you had spelled the original type. The only difference is what a reader and `go doc` see at the declaration site.
saying these in an interview costs you the question
- Says an alias creates a new type with its own identity
- Thinks a defined type and its underlying type are interchangeable
- Believes a defined type adds boxing or a runtime wrapper
- Claims you can declare methods on an alias to float64
- Uses the words alias and defined type interchangeably in review