Pointer Safety
Go has pointers but no pointer arithmetic, and it collects garbage, so most of the ways a pointer can hurt you in C are gone. Three things are left, and this challenge is all three.
Returning the address of a local
func NewAccount(owner string, balance int) *Account {
return &Account{Owner: owner, Balance: balance}
}
In C that is a dangling pointer. In Go it is the standard way to write a constructor. The compiler runs an escape analysis, sees the address outlive the function, and allocates the value on the heap instead of the stack. You never say which, and you cannot get it wrong.
A nil pointer is a usable receiver
Calling a method on a nil pointer does not panic by itself. The method runs with a nil receiver, and only panics if it goes on to read a field:
func (a *Account) Deposit(n int) bool {
if a == nil {
return false
}
a.Balance += n
return true
}
So a pointer-receiver method can define what "no value" means. That is how
(*T).String methods stay printable and how a nil *Node can be a valid empty
tree. The rule is only that the method must check before it dereferences.
Note the difference from a value receiver: there is no such thing as a nil
Account, so a value-receiver method can never be in this position.
Aliasing: a pointer into a slice
&accounts[i] is the address of the element itself, not of a copy. Returning
one hands the caller a live view of the slice:
p := Richest(accounts)
p.Deposit(100) // accounts[i] changed too
That is deliberate here, and it is also the sharpest edge in the exercise. A
pointer into a slice keeps the whole backing array alive, and append may
reallocate that array, after which the pointer refers to the old one and the
two silently stop agreeing. Take pointers into a slice you are not going to
grow.
Clone is the opposite move: copied := *a dereferences into a new value, and
&copied is a pointer nobody else holds. Note that this is a shallow copy -
it copies each field, so a pointer field would still be shared.
Task
Fill in main.go. NewAccount and Clone make new pointers; Deposit,
Describe and BalanceOr handle nil; TopUp, Richest and Addresses work
through the slice rather than through copies; Total skips nils.
Hints
Richestmust return&accounts[best]. Building a localbest := accounts[0]and returning&bestreturns the address of a copy, and the aliasing test will say so.- It also cannot start from a zero
Account: balances can be negative, so the running best has to start ataccounts[0]after a length check. Compare with>so a tie keeps the earlier account. Addressesneedsfor i := range accountsand&accounts[i]. Under Go 1.22 and laterfor _, a := rangegives a freshaeach iteration, so&ayields distinct pointers - but they point at copies, not at the slice, which the test checks.Describeon the zero account is": 0", not"<no account>". Only a nil pointer is missing; an account with an empty name and no money is a real one.BalanceOrfollows the same line:&Account{}returns 0, not the fallback.