惯用的golang:访问和更新结构中的项目

So I am trying to write some code that allows me to edit values in an array in a struct. This example uses "Status" as a possible value to alter, but this is just a simplification to try to get my intent across.

package main

import(
  "fmt"
)

type Parent struct {
  Children []Child
}

type Child struct {
  Status string
}

func (p *Parent) Add() *Child {
  var child Child
  child.Status = "1"
  p.Children = append(p.Children, child)
  return &p.Children[len(p.Children)-1]
}

func main() {
  var p Parent
  child := p.Add()
  child.Status = "2"
  fmt.Println(p)
  fmt.Println(child)
}

This doesn't feel "proper". How should I do this in golang? Certainly I could pass the value in as a parameter, but in my particular case I would like to edit function pointers that are inside the Child struct (not in this code to keep it short) after having added the child. That feels nicer, but maybe I just need to pass them as parameters to the Add method?

eg

func (p *Parent) Add(fn1 func(), fn2 func()) *Child {

Anyway just wondering if anybody has any thoughts on this type of situation.

I think you should create the Child outside of the Add method and pass it in. If you want to manipulate the Child, do that before you passed it in. You might use methods on the Child struct to do that:

func (c *Child) Init(fn1 func(), fn2 func()) {
  c.Status = "1"
  ...
}

func (p *Parent) Add(c *Child) *Child {
  p.Children = append(p.Children, c)
  return c
}

func main() {
  var p Parent
  var child Child
  child.Init(...)       // <- pass something in there...
  p.Add(&child)
  child.Status = "2"
  fmt.Println(p)
  fmt.Println(child)
}

I'd suggest doing this in the simplest way possible. Remember the KISS principle. One part of the system does one and only one thing.

Following this logic, your Add(c *Child) method should only add a child. Creating a Child should be done separately, same for giving a Child some unique properties.

For your OpenGL menu system, this approach fits nicely as well:

m := NewMenu()
t := NewText(text)
t.OnClick = someCallback
// Some other t initialisation.
m.Add(t)
// Some time later you can still change t if it's a pointer and m.Add
// doesn't copy.
t.OnHover = someOtherCallback

The decision on whether to keep your labels exported, or hide them and provide getters/setters is up to you, and depends solely on your system's design and consistency requirements.

Ok the following works for me. The code is changed again to try to focus more clearly on the particular issue I was having. It all comes down to properly using pointers. Thanks for the suggestions.

package main

import (
    "fmt"
)

type HairColor func() string
type Parent struct {
    Children []*Child
}

type Child struct {
    Age           int
    ShowHairColor HairColor
}

func (p *Parent) Add(c *Child) {
    p.Children = append(p.Children, c)
}

func main() {
    var parent Parent
    var child Child

    child.Age = 10
    parent.Add(&child)

    child.ShowHairColor = func() string {
        return "red"
    }
    fmt.Printf("%v
", parent.Children[0])
    fmt.Printf("%v
", child)

    fmt.Println(parent.Children[0].ShowHairColor())
}