Can you modify a struct stored inside an interface? What does this print?
Question 108HardGo 1.22 to 1.25
type P struct{ X int }
func main() {
var i any = P{1}
// i.(P).X = 2 // compile error: cannot assign to i.(P).X
// // (neither addressable nor a map index expression)
p := i.(P) // p is a copy
p.X = 2
fmt.Println(i) // ?
var j any = &P{1}
j.(*P).X = 2 // OK: modifies the pointee
fmt.Println(j.(*P).X) // ?
}
Output:
{1}
2
Assigning P{1} to i copies the struct into storage the interface owns. The result of a type assertion is a fresh, non-addressable value, so you cannot assign through it, and the local variable p is a second copy. The runtime also relies on this: several interface values may share one boxed copy, so in-place mutation would be unsafe.
To mutate, either store a pointer in the interface (any(&P{})), or read, modify and write back (p := i.(P); p.X = 2; i = p). The same rule is why a pointer-receiver method cannot be called on a value held in an interface, and why reflect.ValueOf(i).Field(0).SetInt(2) panics with "unaddressable value" when i holds a struct rather than a pointer.
More on Interfaces, Methods & Embedding
- Q106How does
http.HandlerFuncwork? Explain the function-type adapter pattern. - Q107What happens when you call a value-receiver method through an interface that holds a nil pointer?
- Q109How do you write a generic function that needs to call a pointer-receiver method on
T? (thePT interface{ *T; M() }pattern) - Q110How do you "seal" an interface so other packages cannot implement it? Can the seal be bypassed?
- Q111How do you check at runtime whether a type implements an interface using reflection? Why is
reflect.TypeOfawkward for interface types?