# Understanding T and *T method receivers in Go

> **Source:** [http://localhost:1313/method-receivers-in-go/](http://localhost:1313/method-receivers-in-go/)
> **Author:** [Vikash Patel](https://vikashpatel.net)
> **Published:** February 23, 2025
> **Reading Time:** 3 min
> 
> *This is the raw Markdown source of the article from the [Lorbic Technical Journal](http://localhost:1313/).*

---


In Go, method receivers determine whether a method acts on a copy of a value or a reference to it. This choice isn't just about performance; it affects correctness and behavior, especially when dealing with synchronization primitives (mutex, wait group, etc), slices, and embedded types.

In this post I explore when to use T (a value receiver) vs. \*T (a pointer receiver) and why, in most cases, pointer receivers should be the default and preferred.

---

#### Understanding T and \*T

In Go, every type T has an associated pointer type \*T. Taking the address of a T variable results in a \*T:

```go
type T struct {
    a int
    b bool
}

var t T     // t is of type T
var p = &t  // p is of type *T
```

T and \*T are distinct types. You can't substitute \*T where T is expected and vice versa.

#### Declaring Methods on Types

Go allows you to declare methods on any type you define in your package. That means you can attach methods to both T and \*T:

```go
func (t T) ValueMethod() { /* operates on a copy */ }
func (t *T) PointerMethod() { /* operates on the original */ }
```

Now the billion dollar question: When should a method use a pointer receiver?

#### When to Use a Pointer Receiver

##### 1. The method modifies the receiver.

If your method changes the state of T, you must use \*T, or modifications won't persist.

```go
func (p *Person) Rename(name string) {
    p.Name = name
}
```

##### 2. The type is large.

Passing large structs by value incurs unnecessary copying.

```go
type BigStruct struct {
    Data [1024]byte
}

func (b BigStruct) BadMethod() { /* Copies 1024 bytes */ }

func (b *BigStruct) GoodMethod() { /* Uses reference of the 1024 bytes */ }
```

##### 3. The type contains synchronization primitives (e.g., sync.Mutex).

Copying a sync.Mutex defeats it purpose of maintaining the state.

```go
type Counter struct {
    mu  sync.Mutex
    val int
}

func (c *Counter) Increment() {
    c.mu.Lock()
    defer c.mu.Unlock()
    c.val++
}
```

Declaring Increment on Counter (not \*Counter) would copy mu, making it useless.

##### 4. The type is meant to be used via pointers.

Some types naturally work as pointers, such as linked list nodes.

##### 5. The type is embedded in another struct.

If a type is meant to be embedded, methods must use pointer receivers to avoid unintended copies. Copies introduce performance penalty.

```go
type Stats struct {
    a, b, c Counter
}

func (s Stats) Sum() int {
    return s.a.Increment() + s.b.Increment() + s.c.Increment() // Copies `Counter`, breaks mutex
}
```

#### When To Use a Value Receiver?

A value receiver is fine if:

- The method does not modify the receiver.
- The type is small and simple (like int, bool, or struct{X, Y int}).
- The type is inherently immutable.

For example:

```go
type Point struct {
    X, Y int
}

func (p Point) Distance(q Point) float64 {
    dx, dy := float64(q.X-p.X), float64(q.Y-p.Y)
    return math.Sqrt(dx*dx + dy*dy)
}
```

Here, Distance doesn't modify Point, and copying two ints is negligible.

#### Should Methods That Don't Mutate Use a Pointer Receiver?

Yes, in most cases. Even if a method doesn't modify the receiver, using \*T avoids unintended copies and maintains consistency across your codebase.
The exceptions:

- Small, immutable types (e.g., Point).
- Types that should be copied intentionally, like function options or temporary configurations.

Basically:

- Default to using \*T (reference receiver) unless you have a good reason not to.
- Use T (value receiver) only when your type is small and meant to be copied.
- Never use T (value receiver) if the type has a sync.Mutex, a slice, or is embedded in another struct.

By following these guidelines, you avoid subtle bugs and unintended performance penalties in your Go code.

Cheers!!
Happy coding.

