how to pass different types to a function - go

I have two golang functions, doing exactly same thing, one takes input a slice and the other takes a map as input. I want to combine this into one function, to avoid duplication.
func DoSomething([]data) {
//do something.
}
func DoSomething(map[string]data) {
//do something.
}
Combined function may look like:
func DoSomethingNew (param type) {
//param could be map or slice
}
I want to know if it is possible to pass different types to same function in golang and how. I googled but couldn't find anything relevant.

You can use interfaces Go Playground
func F(data interface{}) {
switch data.(type){
case []int:
fmt.Println("Slice")
fmt.Println(data)
case map[string]int:
fmt.Println("Map")
fmt.Println(data)
}
fmt.Println()
}
where you actually check for the type and do something based on the type.

There are several ways you could do this, but the simple way would be to make it so DoSomethingNew accepts the interface{} type. Inside of the method you would then do a type switch or in this case with only two options, perhaps just one type assertion, followed by the other, returning error if both fail. Another option would be to have both as arguments and check for nil inside the function with a similar if, else-if, else pattern to handle the error if the input is of neither types you're looking for. To make your code more safe you could move to a more strict interface than the empty one which all types implement. You could also do some method chaining or even implement the method with using the types themselves as the receiving type. Here's an example that shows a few of the ideas; https://play.golang.org/p/_v2AyFjGzv

Related

Golang generic return type on a method

I'm new to golang, and I'm struggling to see how I can get some generic behavior on a method.
func (s *service) getCars(filter Filter) ([]car, error){
var err error
var cars []car
switch filter {
case filter.Toyota:
cars, err = s.dbClient.getToyotas()
case filter.Honda:
cars, err = s.dbClient.getHondas()
}
return cars, nil
}
The objects returned from s.dbClient.getToyotas() and s.dbClient.getHondas() are data structs with the same fields, but have different types. I should note these returned structs are auto generated, so I don't have the ability to alter them.
It looks like go interfaces only allow methods, and not data fields, so I don't if it's even possible to define a car type that can be used as the return type. I looked at generics as well, but it seems that generics are not allowed on struct methods.
What's the idiomatic way of doing something like this in go?
Defining a common interface is the idiomatic way to go.
Access to fields can be "modeled" by defining accessor methods on interface and then implementing those on specific structs.
In fact, it is a better solution as those methods can do more than simple access to internal, private variables. For example, they can have lazy initialization or cached access built in. Each struct can have unique implementation or you can have a base struct with default one.

How to write several implementation of the same method that have a different signature

I have several implementation of the same method SetRateForMeasure:
package repartition
type Repartition interface {
Name() string
Compute(meters []models.Meter, totalsProd, totalsConso map[string]float64) []models.Meter
SetRateForMeasure(meter models.Meter, measure models.Measure, total float64) float64
}
Then, in my code (in repartition.go), I call it:
rate := repartition.SetRateForMeasure(meter, measure, total)
where repartition is the interface defined before.
Thing is, when I add a new implementation of this method, the arguments of my functions might differ.
For example, the static repartition use a static percentage that is only used in this case.
I end up adding parameters so that I have a common interface to all methods, but it results that there is a lot of unused parameters depending on the implementation.
If I add it to common interface, it will be unused for the other definitions.
I tried to remove this method from my interface definition, but now
rate := repartition.SetRateForMeasure()
is no more defined.
How should I organize my code ?
There is no function overloading in Go, so you cannot declare the same function with different arguments. There's a few ways you can implement this though:
You can add multiple functions with different names and signatures
You can change the function to accept a struct instead of arguments
SetRateForMeasure(args SetRateOptions) float64
type SetRateOptions struct {
Meter models.Meter
Measure models.Measure
Total float64
Percentage *float64 // If nil, use default percentage
... // more parameters as needed
}
Go doesn't support method overriding. You either ​define methods with different names that take different parameters
​ or you can declare the method to accept a parameter struct.
type SetRateParams struct {
Meter models.Meter
Measure models.Measure
Total float64
}
type Repartition interface {
SetRateForMeasure(params SetRateParams) float64
}
Optionally, you can declare params in your structs as pointers, so you can represent "not-provided" semantics with nil instead of using the zero-value. This might be relevant in case of numerical params where 0 could be a valid value.
Using a struct param has also the advantage that you don't have to change all the call sites in case you decide to add an additional param 6 months from now (you just add it to the struct).
There are also worse solutions with interface{} varargs, for the sake of stating what is possible, but unless you loathe type safety, I wouldn't recommend that.

how to decode a byte slice to different structs elegantly

var response Response
switch wrapper.Domain {
case "":
response = new(TypeA)
case "TypeB":
response = new(TypeB)
case "TypeC":
response = new(TypeC)
case "TypeD":
response = new(TypeD)
}
_ = decoder.Decode(response)
As shown in the code snippet, I got enough information from the Domain filed of wrapper to determine the type of response, and for each type, the following operations are performed:
create a new instance of that type using new
use the decoder to decode the byte slice to the instance created in step 1
I am wondering if there is a way to make the first step more generic and get rid of the switch statement.
A bit about your code
As per discussion in comments, I would like to share some experience.
I do not see nothing bad in your solution, but there are few options to improve it, depends what you want to do.
Your code looks like classic Factory. The Factory is a pattern, that create object of a single family, based on some input parameters.
In Golang this is commonly used in simpler way as a Factory Method, sometimes called Factory function.
Example:
type Vehicle interface {};
type Car struct {}
func NewCar() Vehicle {
return &Car{}
}
But you can easily expand it to do something like you:
package main
import (
"fmt"
"strings"
)
type Vehicle interface {}
type Car struct {}
type Bike struct {}
type Motorbike struct {}
// NewDrivingLicenseCar returns a car for a user, to perform
// the driving license exam.
func NewDrivingLicenseCar(drivingLicense string) (Vehicle, error) {
switch strings.ToLower(drivingLicense) {
case "car":
return &Car{}, nil
case "motorbike":
return &Motorbike{}, nil
case "bike":
return &Bike{}, nil
default:
return nil, fmt.Errorf("Sorry, We are not allowed to make exam for your type of car: \"%s\"", drivingLicense)
}
}
func main() {
fmt.Println(NewDrivingLicenseCar("Car"))
fmt.Println(NewDrivingLicenseCar("Tank"))
}
Above code produces output:
&{} <nil>
<nil> Sorry, We are not allowed to make exam for your type of car: "Tank"
So probably you can improve your code by:
Closing into a single function, that takes a string and produces the Response object
Adding some validation and the error handling
Giving it some reasonable name.
There are few related patterns to the Factory, which can replace this pattern:
Chain of responsibility
Dispatcher
Visitor
Dependency injection
Reflection?
There is also comment from #icza about Reflection. I agree with him, this is used commonly, and We cannot avoid the reflection in our code, because sometimes things are so dynamic.
But in your scenario it is bad solution because:
You lose compile-time type checking
You have to modify code when you are adding new type, so why not to add new line in this Factory function?
You make your code slower(see references), it adds 50%-100% lose of performance.
You make your code so unreadable and complex
You have to add a much more error handling to cover not trivial errors from reflection.
Of course, you can add a lot of tests to cover a huge number of scenarios. You can support TypeA, TypeB, TypeC in your code and you can cover it with tests, but in production code sometime you can pass TypeXYZ and you will get runtime error if you do not catch it.
Conclusion
There is nothing bad with your switch/case scenario, probably this is the most readable and the easiest way to do what you want to do.
Reference
Factory method: https://www.sohamkamani.com/golang/2018-06-20-golang-factory-patterns/
Classic book about patterns in programming: Design Patterns: Elements of Reusable Object-Oriented Software, Erich Gamma and his band of four, ISBN: 978-0201633610
Reflection benchmarks: https://gist.github.com/crast/61779d00db7bfaa894c70d7693cee505

Is it possible to use only one of the return values when initializing members in Go?

I understand how to use multiple return values in go. I further understand that in most cases one of the returns is an error, so ignoring returned values can be dangerous.
Is there a way to ignore a value in struct initializer like this? The below example does not work as Split returns two values, but I am interested only in the first one. I can of course create a variable but...
someFile := "test/filename.ext"
contrivedStruct := []struct{
parentDir string
}{
{ parentDir: filepath.Split(someFile) },
}
It's not possible to use only one of the return values when initializing members in Go.
Using variables clearly expresses your intent.
Go sometimes feels like it could be more succinct, but the Go authors favoured readability over brevity.
Alternatively, use a wrapper function. There are several 'Must' wrapper functions in the standard library, like: template.Must.
func first(args ...string) string {
return args[0]
}
For your particular example, splitting paths, see filepath.Base or filepath.Dir.
No, there is no way to skip one of the returned values in structure initializer.

Use map[string]SpecificType with method of map[string]SomeInterface into

I get cannot use map[string]MyType literal (type map[string]MyType) as type map[string]IterableWithID in argument to MapToList with the code below, how do I pass in a concrete map type to method that expects a interface type?
https://play.golang.org/p/G7VzMwrRRw
Go's interface convention doesn't quite work the same way as in, say, Java (and the designers apparently didn't like the idea of getters and setters very much :-/ ). So you've got two core problems:
A map[string]Foo is not the same as a map[string]Bar, even if Bar implements Foo, so you have to break it out a bit (use make() beforehand, then assign in a single assignment).
Interface methods are called by value with no pointers, so you really need to do foo = foo.Method(bar) in your callers or get really pointer-happy to implement something like this.
What you can do to more-or-less simulate what you want:
type IterableWithID interface {
SetID(id string) IterableWithID // use as foo = foo.SetID(bar)
}
func (t MyType) SetID(id string) IterableWithID {
t.ID = id
return t
}
...and to deal with the typing problem
t := make(map[string]IterableWithID)
t["foo"] = MyType{}
MapToList(t) // This is a map[string]IterableWithID, so compiler's happy.
...and finally...
value = value.SetID(key) // We set back the copy of the value we mutated
The final value= deals with the fact that the method gets a fresh copy of the value object, so the original would be untouched by your method (the change would simply vanish).
Updated code on the Go Playground
...but it's not particularly idiomatic Go--they really want you to just reference struct members rather than use Java-style mutators in interfaces (though TBH I'm not so keen on that little detail--mutators are supes handy to do validation).
You can't do what you want to do because the two map types are different. It doesn't matter that the element type of one is a type that implements the interface which is the element type of the other. The map type that you pass into the function has to be map[string]IterableWithID. You could create a map of that type, assign values of type MyType to the map, and pass that to the function.
See https://play.golang.org/p/NfsTlunHkW
Also, you probably don't want to be returning a pointer to a slice in MapToList. Just return the slice itself. A slice contains a reference to the underlying array.

Resources