I’m trying to learn generics and trying to put it all together, and I can’t seem to understand it. Can I use the union type and get access to the properties inside the struct from an interface?
# command-line-arguments
./main.go:83:15: shape.length undefined (type T has no field or method length)
./main.go:83:30: shape.height undefined (type T has no field or method height)
Interfaces in Go are just method sets; struct fields are not part of the interface, so you cannot write a generic getArea function that way.
In your case, I don’t actually see a use case for generics: Just interfaces. Shape can be redefined as an interface with Length and Height methods and getArea can use those: Go Playground - The Go Programming Language
As for trying to learn them, I’m not sure I have any helpful guidelines. I think I have two places where I wrote code that uses generics in my own code:
The first example might be useful to you to see how it works: The Work function receives two main parameters:
A channel of some generic type (probably a struct of parameters, maybe simpler like a string, etc.),
A worker function that processes an element from the channel and returns a result.
Given those two things, the Work function returns a channel of results whose type is determined by the worker function.
I notice I don’t have any tests to demonstrate how it works…
The Each function is more specific to another project I’m working on, so it’s probably less useful to you than the workers example. I only included it to demonstrate that, at least for me, generics are not super useful.
Thank you so much for the reply. That Go playground is super helpful! I come from the Typescript land, so definitely different. So my initial thought was, what if I had a Shape, like circle, square, rectangle, etc., and then they would have some sort of filtering? This is obviously an arbitrary example, but I was trying to understand what’s possible with interfaces and structs.
So my original example was something like this.
type rectangle struct {
length, height float64
}
type square struct {
length, height float64
}
type circle struct {
radius float64
}
type Shape interface {
rectangle | square | circle
}
func isCircle[T Shape](x T) bool {
_, ok := interface{}(x).(circle)
return ok
}
func getArea[T Shape](shape T) float64 {
if isCircle(shape) {
return 2 * math.Pi * shape.radius
}
return shape.length * shape.height
}
func main() {
rect := rectangle{1.3, 4.3}
sqr := square{3.2, 3.4}
circ := circle{3.2}
getArea(rect)
getArea(sqr)
getArea(circ)
}
Though I like how you’ve implemented it, and my example introduces some code smell
It does feel more verbose but the codes are a lot cleaner that way.
If this needs to work for non-rectangles, too, I think getArea should no longer be a standalone function and should instead be on the Shape interface. Otherwise, you will need to change getArea’s implementation whenever you need to add a new shape (e.g. ellipses, or other polygons, or “free-form” shapes with or without curves, etc.). If you move getArea into an Area function on Shape, not only do you no longer need getArea, but isCircle, as well: Go Playground - The Go Programming Language