how to make golang function param support different but similar struct
In Golang, if you want to have a function that accepts different but similar structs, you'll need to use interface. Interface is a type in go that defines and groups method signatures. Any type that implements those methods is said to satisfy that interface.
Here's an example of how to do it:
type Animal interface {
Speak() string
}
type Dog struct {
Name string
}
type Cat struct {
Name string
}
func (d Dog) Speak() string {
return d.Name + " says woof"
}
func (c Cat) Speak() string {
return c.Name + " says meow"
}
func Talk(a Animal) {
fmt.Println(a.Speak())
}
func main() {
Talk(Dog{Name: "Rex"}) // Outputs: Rex says woof
Talk(Cat{Name: "Fluffy"}) // Outputs: Fluffy says meow
}
This way, you're making Golang support different but similar structs as function parameters.
I don't want to add extra function/property to the original struct
If you don't want to alter your original structs, another option in Golang is to use the interface{}
type, which can accept any type, including any struct.
Here's an example:
type Dog struct {
Name string
}
type Cat struct {
Name string
}
func PrintName(v interface{}) {
switch value := v.(type) {
case Dog:
fmt.Println("Dog's name is", value.Name)
case Cat:
fmt.Println("Cat's name is", value.Name)
default:
fmt.Println("Not a Dog or Cat")
}
}
func main() {
PrintName(Dog{Name: "Rex"})
PrintName(Cat{Name: "Fluffy"})
}
In this example, the function PrintName
accepts a parameter of type interface{}
, then uses a type switch to determine the actual type of the value and print the Name field accordingly.
This method will work for any struct type you pass, provided you handle the type in the type switch. However, it does not enforce type safety and requires you to manually handle each possible type.
I also want to use reflect on the struct, interface seems not to support reflect
You can use reflection in Go to operate on structs passed in as interface{}
type. You can get the value of a struct's field by its name using the reflect
package.
Here is an example:
package main
import (
"fmt"
"reflect"
)
type Dog struct {
Name string
}
type Cat struct {
Name string
}
func PrintName(v interface{}) {
val := reflect.ValueOf(v)
nameField := val.FieldByName("Name")
if nameField.IsValid() {
fmt.Println("Name is", nameField.String())
} else {
fmt.Println("No name field")
}
}
func main() {
PrintName(Dog{Name: "Rex"})
PrintName(Cat{Name: "Fluffy"})
}
In this example, the PrintName
function uses reflection to get the struct's Name
field by its name. If the struct does not have a Name
field, FieldByName
returns a zero Value
, and IsValid
returns false
, so it will print "No name field".
when invoking PrintName, if I pass a pointer of struct, will it work
In the code provided, if you pass a pointer to a struct to the PrintName
function, it wouldn't work. This is because reflect.ValueOf(v)
does not dereference pointers.
To handle this case, you need to add an extra check to see if the passed value is a pointer, and if so, dereference it.
Here's how you can modify PrintName
function:
func PrintName(v interface{}) {
val := reflect.ValueOf(v)
if val.Kind() == reflect.Ptr {
val = val.Elem()
}
nameField := val.FieldByName("Name")
if nameField.IsValid() {
fmt.Println("Name is", nameField.String())
} else {
fmt.Println("No name field")
}
}
Now the function will correctly handle both structs and pointers to structs:
func main() {
dog := Dog{Name: "Rex"}
PrintName(&dog) // Outputs: Name is Rex
cat := Cat{Name: "Fluffy"}
PrintName(&cat) // Outputs: Name is Fluffy
}