-1

I have the following struct that contains channels and a map for storage of data. I want to be able to pass that struct into functions in order to make use of those channels so that once they are triggered/have incoming messages, to use them in order to update the map that is associated with it.

I understand that maps by default are passed by reference when sent to various functions. Would this be the same case even when they are contained within a custom struct? How do i make sure that my entire struct is passed around to functions by reference in order to update Storage and also make use of its channels?

type CustomStrct struct {
Storage      map[string]string
RetrieveChannel    chan string
InsertChannel      chan string
}

This is a constructor I have created for initialising a new instance of the struct:

func InitializeNewStore() CustomStrct {
    newCustomStruct := CustomStrct {
        Storage:      make(map[string]string),
        RetrieveChannel:    make(chan Request),
        InsertChannel:    make(chan Request),
       }
 
return newCustomStruct 
}
AC007
  • 137
  • 9
  • 1
    Go does not have value references. To achieve what you want, pass a pointer to your strict. A go map is essentially a pointer. – colm.anseo Feb 06 '22 at 19:19
  • I had just thought of that while typing up my question . Does that mean that I should be passing variables into my constructor and creating a new instance of my Struct using those passed in variables ? – AC007 Feb 06 '22 at 19:24
  • For example : m:= make(map[string]string ) c1:= make(chan string ). NewStruct := InitializeNewStore(m,c1) ? – AC007 Feb 06 '22 at 19:26
  • 1
    Your constructor is fine. Channels & maps need to be initialized & doing it there before the `struct` is used, is the perfect place it. – colm.anseo Feb 06 '22 at 19:28
  • I appreciate your help but unfortunately I’m not following what you’re saying . Could you please show me via a code example ? – AC007 Feb 06 '22 at 19:32
  • 1
    Channel and map values are a single internal pointer. That internal pointer does not change no matter how the value is passed around (in a struct field or some other way). The code in InitializeNewStore is a good example of how to initialize the channels and maps before the struct is used. – Charlie Tumahai Feb 06 '22 at 19:39
  • @BaytaDarell So if I wanted to add a value to the map in my struct would the following function work? : func AddToMap(cs *CustomStruct){ cs.Storage["foo"]="foo" } – AC007 Feb 06 '22 at 19:41
  • 1
    Yes, that function works. This function also works: `func AddToMap(cs CustomStruct){ cs.Storage["foo"]="foo" }`. The key point to understand is that maps and channels are internally a pointer. The pointer can be copied and still refer to the channel or map's data. – Charlie Tumahai Feb 06 '22 at 19:45
  • Thank you very much for the help and information ! :) – AC007 Feb 06 '22 at 19:46
  • Do make sure you synchronized access to any maps. Either an individual sync.Mutex for each map or one single mutex for the entire struct. – colm.anseo Feb 06 '22 at 20:57

1 Answers1

2

Slices, maps and channels are pointer-like values in Go: copying a struct containing a channel copies a reference to the channel, not the channel itself:

a := CustomStrct{
    RetrieveChannel: make(chan Request),
}
b := a
log.Println(a.RetrieveChannel == b.RetrieveChannel)    // logs true

So it's quite fine to pass your struct either by value or by reference.

If you need to ensure that go vet will flag attempts to pass your struct by value, the simplest solution is to embed a sync.Mutex inside the struct:

type CustomStrct struct {
    mu sync.Mutex
    ...
}

You don't need to actually use the mutex: just having it embedded in the struct will cause go vet to complain whenever you attempt to pass it by value.

jch
  • 5,382
  • 22
  • 41