How to generalize parameters for a function

562 views
Skip to first unread message

Frank Jüdes

unread,
Mar 14, 2023, 9:27:05 PM3/14/23
to golang-nuts
Hi Friends,

i still somewhat new with Go and need a little tutoring: I have to create Excel files, using the excelize package and wrote a little function to store data from a map[string]int64 into a column within the worksheet:

// -------------------------------------------------------------------------------------------------
// Store a slice of int64 data items into the cells of a column
// -------------------------------------------------------------------------------------------------
func SetColumnValuesInt64(xf *excelize.File,
      p_SheetName string,
      p_StartRow int,
      p_Column rune,
      p_CellStyle *excelize.Style,
      p_Keys *UTL_StringSlice.T_StringSlice,
      p_Values *map[string]int64) {
   Row := p_StartRow
   StyleID,_ := xf.NewStyle(p_CellStyle)
   for _,Key := range(*p_Keys) {
     CellAddress := fmt.Sprintf("%c%d",p_Column,Row)
     xf.SetCellValue(p_SheetName,CellAddress,(*p_Values)[Key])
     xf.SetCellStyle(p_SheetName,CellAddress,CellAddress,StyleID)
    Row++
   } // END for
} // END SetColumnValuesInt64

Basically the function iterates through a slice of strings, containing the key-values for the data-map and then calls the SetCellValue function from the excelize package to store the number into the cell.
Now i have another set of data, stored into a map[string]float64
So i duplicated the above function, just replacing the red line with
      p_Values *map[string]float64) {

Basically it is the same code, just the parameter-type is different. I looked into the definition of the function SetCellValue  and saw that it is using the empty interface as parameter for the value, so i tried to define my function with
      p_Values *map[string]interface{}) {

The function compiles fine, but if i try to use it with a *map[string]int64 as a parameter, the compiler objectst with the message »cannot use Amounts (variable of type *map[string]int64) as type *map[string]interface{} in argument to ExcelFormats.SetColumnValues«

Can somebody please give me hint what i am doing wrong?

Thank you very much in advance for your help.

Best regards from Charleston (WV),
     Frank/2

Ian Lance Taylor

unread,
Mar 14, 2023, 9:40:27 PM3/14/23
to Frank Jüdes, golang-nuts
First I'll note that it's quite unusual to use a pointer to a map.
Maps are reference types. If you pass a map to a function, and that
function adds values to the map, the caller will also see the new map
entries.

That said, the error message seems clear enough: a function that
expects a pointer to map[string]any can't take an argument that is a
pointer to map[string]int64. The types any and int64 are different
types. You can't interchange them. See
https://go.dev/doc/faq#covariant_types for a related issue.

Ian

Landon Cooper

unread,
Mar 14, 2023, 10:03:09 PM3/14/23
to golang-nuts
Hi Frank, the issue is that the compiler won't accept a map[string]float64 as a map[string]interface{} because they are defined as different types.
It probably feels to you as though the code would work okay because you expect to take things out of the map, check their type and then use them appropriately.
However, the compiler doesn't attempt to check that's really what you're doing..

Instead, the compiler has to assume your code might try to use the map to place things into it.
If you ask for a map[string]interface{}, are given a map[string]float64, and then try to put tulips into it, the code will fail.

This is an example of type variance, for which Ian has linked a much nicer and more concise answer than I could hope to type out.

Frank Jüdes

unread,
Mar 14, 2023, 10:06:54 PM3/14/23
to golang-nuts
Thank you very much Ian!
I am using a pointer to the map because i want to pass the map by reference, not by value.
Those maps can become pretty large and are being handed down through a couple of function calls and i don't want them to be copied over and over again.

I read the document you referenced, but am not able to understand…
So i can use interface{} or any just to generalize simple types?
Why is the compiler accepting then a p_Values map[string]interface{} as a parameter of a function? - What would be a compatible data-type to pass as this parameter?

Thank you very much in advance for your help.
Best regards from Charleston (WV),
     Frank/2


Kurtis Rader

unread,
Mar 14, 2023, 10:57:13 PM3/14/23
to Frank Jüdes, golang-nuts
On Tue, Mar 14, 2023 at 7:07 PM Frank Jüdes <jue...@gmail.com> wrote:
Thank you very much Ian!
I am using a pointer to the map because i want to pass the map by reference, not by value.
Those maps can become pretty large and are being handed down through a couple of function calls and i don't want them to be copied over and over again.

Maps are a special-case. You can't pass them "by value" in the sense you mean because a "map" value is a very tiny structure that contains a pointer to the actual map. Possibly the oldest public discussion of the fact maps are reference type is this blog post: https://go.dev/blog/maps. Using a pointer does, in fact, avoid copying that very small data structure but is not necessary to keep from copying the entire map.
 
I read the document you referenced, but am not able to understand…
So i can use interface{} or any just to generalize simple types?
Why is the compiler accepting then a p_Values map[string]interface{} as a parameter of a function? - What would be a compatible data-type to pass as this parameter?

The compiler accepts a type of "map[string]interface{}" because that is well defined and obviously useful, Functions, such as those in the fmt package, would not be useful if a function could not deal with the "interface{}" type directly or indirectly. Your confusion is very common and arises from the misconception that the Go map implementation will magically convert map values from a concrete type (e.g., "int") to the "any" type if that is what is needed in a particular context. One reason it doesn't do so is the same reason it doesn't auto-magically convert any other map value type; e.g., "int" to "float".

--
Kurtis Rader
Caretaker of the exceptional canines Junior and Hank

Frank Jüdes

unread,
Mar 15, 2023, 5:51:10 AM3/15/23
to golang-nuts
Hi Kurtis,
good to know that a map will always be "kind of" passed by reference to functions. I have verified that with a short test-program on the playground -> https://go.dev/play/p/HxdgpM-QozM
So in the next code-review all those *[maptype] references will be removed. Things are very different in this case in other programming languages…

I understand the misconception and that a map[type]interface{} won't automagically convert the data-type from the parameter into anything else. My intention was to explain the compiler to just accept whatever is fed into the function and just pass the data through to a function that uses interface{} for a single parameter. That's all this function needs to do: Accept a map of "something", get single values of "something" and pass them on to another function that accepts a single parameter of "something".

I think i have found a solution in using a "generic" interface, stumbled upon this web-site: https://www.geeksforgeeks.org/generics-in-golang/?ref=rp

And created a little test-program: https://go.dev/play/p/mOu1pmmfdX8

This should allow me to pass different types of maps - actually i only need three types - to a generic function that passes the values on to another (generic) function.

Thank you very much for your help.

Best regards from Charleston (WV),
     Frank/2

Frank Jüdes

unread,
Mar 15, 2023, 6:03:43 AM3/15/23
to golang-nuts
Well, that didn't go very far: https://go.dev/play/p/UtsrRar9J0Q
Fails with the messages
./prog.go:15:11: invalid operation: cannot index p_Map (variable of type MAP constrained by DataMap)
./prog.go:16:26: cannot range over p_Map (variable of type MAP constrained by DataMap) (MAP has no core type)

I think i have to to some more research here - or just copy and paste three different typed functions…

Best regards from Charleston (WV),
     Frank/2

Jan Mercl

unread,
Mar 15, 2023, 6:07:16 AM3/15/23
to Frank Jüdes, golang-nuts
On Wed, Mar 15, 2023 at 11:03 AM Frank Jüdes <jue...@gmail.com> wrote:
>
> Well, that didn't go very far: https://go.dev/play/p/UtsrRar9J0Q
> Fails with the messages
> ./prog.go:15:11: invalid operation: cannot index p_Map (variable of type MAP constrained by DataMap)
> ./prog.go:16:26: cannot range over p_Map (variable of type MAP constrained by DataMap) (MAP has no core type)

https://go.dev/play/p/6KN36eeomML

Dan Kortschak

unread,
Mar 15, 2023, 6:58:09 AM3/15/23
to golan...@googlegroups.com
On Tue, 2023-03-14 at 19:56 -0700, Kurtis Rader wrote:
> Maps are a special-case. You can't pass them "by value" in the sense
> you mean because a "map" value is a very tiny structure that contains
> a pointer to the actual map.

The passed value is a pointer to the header, otherwise changes to maps
would require the altered map to be returned as with slices and append.

https://go.dev/play/p/avBBkNwVZRY

Brian Candler

unread,
Mar 15, 2023, 7:31:09 AM3/15/23
to golang-nuts
Which is also the reason why you can't insert into a nil map - you have to make() one.

Frank Juedes

unread,
Mar 15, 2023, 12:50:14 PM3/15/23
to Jan Mercl, golang-nuts

Hi Jan,

that looks great and is exactly what i was looking for, just need to wrap my head around that function declaration… I understand that declaration, but do not fully understand this in general. I guess i will have to do some more reading…

Thank you very much!

Frank Juedes

unread,
Mar 15, 2023, 12:56:05 PM3/15/23
to Dan Kortschak, golan...@googlegroups.com

Hi Dan,

Thank you very much for your answer, so that's the data structure behind maps, very interesting.

I had actually thought about using unsafe pointers and then type-casting, but that is how i would have done it in the C-dekades, not with Go.

Dan Kortschak

unread,
Mar 15, 2023, 2:44:51 PM3/15/23
to golan...@googlegroups.com
On Wed, 2023-03-15 at 12:55 -0400, Frank Juedes wrote:
> >
> Hi Dan,
> Thank you very much for your answer, so that's the data structure
> behind maps, very interesting.
> I had actually thought about using unsafe pointers and then type-
> casting, but that is how i would have done it in the C-dekades, not
> with Go.

The source for this is here
https://cs.opensource.google/go/go/+/refs/tags/go1.20.2:src/runtime/map.go

Reply all
Reply to author
Forward
0 new messages