[generics] How to use maps in generic structs?

1,112 views
Skip to first unread message

Markus Heukelom

unread,
Aug 10, 2020, 8:49:48 AM8/10/20
to golang-nuts
This is invalid: 

type BiMap[type V, K] struct {
forward map[K]V
reverse map[V]K
}

How do I specify an interface constraint for V,K such that this is valid? Is that possible at all?


Carla Pfaff

unread,
Aug 10, 2020, 9:45:25 AM8/10/20
to golang-nuts
K and V must be comparable, since you use them as map keys:

type BiMap[type V, K comparable] struct {

Markus Heukelom

unread,
Aug 10, 2020, 10:08:28 AM8/10/20
to Carla Pfaff, golang-nuts
Just a quick thought, instead of "comparable" (and type lists) maybe we could use a single operator to specify the required "operators" interface:

type BiMap[type V, K ==] struct {
forward map[K]V
reverse map[V]K
}

func Max[type T <](a, b T) T {

}

func ScanRowStruct[type T .](rows sql.Rows, dest *T) error {  // operator . ensures T is a struct

}




--
You received this message because you are subscribed to a topic in the Google Groups "golang-nuts" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/golang-nuts/dwZnPgTC7So/unsubscribe.
To unsubscribe from this group and all its topics, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/202f1ffa-ac89-42eb-b617-d82e14c537een%40googlegroups.com.

Alvadron

unread,
Aug 10, 2020, 10:08:56 AM8/10/20
to golang-nuts
The type of map keys in Go should be comparable. As you haven't specified any restriction to the type parameters, it doesn't compile because Go doesn't know whether K and V are comparable.

If you add the restriction  "comparable", then it compiles:

type BiMap[type V, K comparable] struct {
forward map[K]V
reverse map[V]K
}

PD: It is true that the error could be more informative. Right now we get:

type checking failed for main 
prog.go2:13:14: invalid map key type K 
prog.go2:14:14: invalid map key type V

Not sure if it is possible but, ideally, the message should say something like "Invalid mp key type K: it is not comparable"

Ian Lance Taylor

unread,
Aug 10, 2020, 2:22:32 PM8/10/20
to Alvadron, golang-nuts
On Mon, Aug 10, 2020 at 7:08 AM Alvadron <klos...@gmail.com> wrote:
>
> The type of map keys in Go should be comparable. As you haven't specified any restriction to the type parameters, it doesn't compile because Go doesn't know whether K and V are comparable.
>
> If you add the restriction "comparable", then it compiles:
>
> type BiMap[type V, K comparable] struct {
> forward map[K]V
> reverse map[V]K
> }
>
> PD: It is true that the error could be more informative. Right now we get:
>
> type checking failed for main
> prog.go2:13:14: invalid map key type K
> prog.go2:14:14: invalid map key type V
>
> Not sure if it is possible but, ideally, the message should say something like "Invalid mp key type K: it is not comparable"

Getting a better error message is https://golang.org/issue/40551.

Ian

Ian Lance Taylor

unread,
Aug 10, 2020, 2:25:40 PM8/10/20
to Markus Heukelom, Carla Pfaff, golang-nuts
On Mon, Aug 10, 2020 at 7:08 AM Markus Heukelom
<markus....@gain.pro> wrote:
>
> Just a quick thought, instead of "comparable" (and type lists) maybe we could use a single operator to specify the required "operators" interface:
>
> type BiMap[type V, K ==] struct {
> forward map[K]V
> reverse map[V]K
> }
>
> func Max[type T <](a, b T) T {
>
> }
>
> func ScanRowStruct[type T .](rows sql.Rows, dest *T) error { // operator . ensures T is a struct
>
> }

It could probably work, but we wind up with a bunch of special purpose
syntax with only a tenuous connection to the way constraints work in
general. And you have to define connections between operators. That
is, comparable supports both == and !=, but you only mentioned ==;
does == imply !=?

Ian

Markus Heukelom

unread,
Aug 11, 2020, 4:24:58 AM8/11/20
to Ian Lance Taylor, Carla Pfaff, golang-nuts
I would even be in favor of not allowing any operations in generic code on T (besides what is possible on all types), allowing T to carry data only*. However, that's admittelly being too restrictive. You really do need to be able to specify the requirement of ==  to support maps in structs**. No way around it. The case of "<" is also strong for functions like max etc. 

So you could maybe even say: you can specify one operator to use with T and that's it (not even allowing interface{}-d constraints and not implying other operators). That's the simplest thing that could possibly make it work for maps, and most generic algorithms that are really wanted. Of course "==" and "<" are just shorter ways for "comparable" and "ordered" and have the slight advantage of not introducing new names in the global namespace. But both would work.

-Markus

* To me, constraints on T are really complex especially because now you have to choose whether to just use plain old interfaces or generics with interface constraints when designing a solution. But being able to use T instead of interface{} really helps in writing clear and type safe code. It's the constraints that make it tricky for me.

** Assuming maps don't use !=

Ian
Reply all
Reply to author
Forward
0 new messages