How to Add Methods to Built-In Types in Go

You cannot add methods directly to built-in types like `int`, `string`, or `bool` in Go because the language only allows method receivers on types defined within the same package.

You cannot add methods directly to built-in types like int, string, or bool in Go because the language only allows method receivers on types defined within the same package. To achieve this functionality, you must define a new type that aliases the built-in type and then attach methods to that new type.

This pattern is standard in the Go ecosystem. For example, the standard library defines time.Duration as an alias for int64 to add methods like Hours() or String(). Here is a practical example of creating a custom type with a method:

package main

import "fmt"

// Define a new type based on a built-in type
type MyInt int

// Add a method to the new type
// Note: The receiver must be MyInt, not int
func (m MyInt) Double() MyInt {
	return m * 2
}

func main() {
	var x MyInt = 5
	fmt.Println(x.Double()) // Output: 10

	// This will fail: cannot define method on built-in int
	// var y int = 5
	// y.Double() // Compile error: undefined
}

If you need to use this new type with existing functions that expect the built-in type, you can perform an explicit conversion. Go allows zero-cost conversions between identical underlying types:

func processNumber(n int) {
	fmt.Println("Processing:", n)
}

func main() {
	var m MyInt = 10
	
	// Convert MyInt back to int to use with standard functions
	processNumber(int(m))
	
	// Convert int to MyInt to use custom methods
	var standardInt int = 20
	var custom MyInt = MyInt(standardInt)
	fmt.Println(custom.Double()) // Output: 40
}

Be careful not to confuse type aliases with type definitions. Using type MyInt = int creates an alias, meaning MyInt and int are identical, and you cannot add methods to MyInt because it is just another name for int. You must use type MyInt int to create a distinct type with the same underlying representation.

This approach is also useful for adding validation or domain-specific logic to primitive types. For instance, you might define type Price float64 and add a Tax() method, ensuring that price calculations are always handled consistently throughout your codebase without polluting the global float64 type.