Pointers
TypeScript does not have explicit pointers
Section titled “TypeScript does not have explicit pointers”In TypeScript (and JavaScript), objects are always passed by reference automatically. You never write “give me the address of this variable.” The runtime handles memory invisibly.
Go exposes memory addresses explicitly. You choose whether a function receives a copy of a value or a pointer to the original. That choice has real consequences for correctness and performance.
The & and * operators
Section titled “The & and * operators”&x gives you a pointer to x — the memory address where x lives.
*p dereferences pointer p — it reads or writes the value at that address.
// TypeScript — objects are implicitly sharedfunction increment(obj: { count: number }) { obj.count++; // mutates the original (reference)}
const counter = { count: 0 };increment(counter);console.log(counter.count); // 1
// BUT primitives are always copied:function addOne(n: number): number { return n + 1; // n is a copy — caller unchanged}let x = 5;addOne(x);console.log(x); // still 5// Go — primitives copied by default; use * to opt in to mutationfunc increment(n *int) { *n++ // dereference and mutate}
func main() { x := 42 increment(&x) // pass address of x fmt.Println(x) // 43 — x was mutated
// Without pointer — value is copied, caller unchanged // increment(x) would be a compile error (type mismatch)}Pointer types
Section titled “Pointer types”Every type T has a corresponding pointer type *T. A pointer variable holds an address, not a value.
// TypeScript has no pointer types// The closest is a wrapper object or ref pattern:const ref = { current: 0 }; // manual "pointer" idiomvar p *int // p is a pointer to int, value is nilx := 10p = &x // p now holds the address of xfmt.Println(*p) // 10 — dereference to read*p = 20 // write through pointerfmt.Println(x) // 20When to use pointers in Go
Section titled “When to use pointers in Go”There are two main reasons to use a pointer:
- Mutation — you want a function to modify the caller’s variable (not work on a copy).
- Efficiency — the struct is large and copying it on every call would be wasteful.
For small structs (2–3 fields) passed read-only, value semantics are usually fine. For large structs or anywhere you need mutation, use a pointer.
// TypeScript — structs/objects always pass by reference// You have no choice; the runtime decidesinterface Config { host: string; port: number; }function connect(cfg: Config) { /* cfg is a reference */ }type Config struct { Host string Port int}
// Value receiver — method gets a copy; cannot modify originalfunc (c Config) String() string { return fmt.Sprintf("%s:%d", c.Host, c.Port)}
// Pointer receiver — method can modify the originalfunc (c *Config) SetPort(p int) { c.Port = p}
func main() { cfg := Config{Host: "localhost", Port: 8080} cfg.SetPort(9090) fmt.Println(cfg.String()) // localhost:9090}Try it
Section titled “Try it”package main
import "fmt"
func increment(n *int) { *n++}
func swap(a, b *int) { *a, *b = *b, *a}
func main() { x := 42 fmt.Println("before:", x) increment(&x) fmt.Println("after increment:", x)
a, b := 10, 20 fmt.Printf("before swap: a=%d b=%d\n", a, b) swap(&a, &b) fmt.Printf("after swap: a=%d b=%d\n", a, b)
// Pointer to struct type Point struct{ X, Y int } p := &Point{X: 1, Y: 2} p.X = 99 // Go auto-dereferences: (*p).X = 99 fmt.Println("point:", *p)}Loading Go runtime (first run only, ~8 MB)…