Golang Parameters conversion - go

Can somebody explain how can this happened?
I put interface as parameter in a function. While invoking this function I pass struct into it, but it didn't give me error. Here's the code
package main
import (
"fmt"
"github.com/myusername/gomodel/domain"
"github.com/myusername/gomodel/model"
)
func main() {
db := model.InitDB()
newFunc(db)
}
func newFunc(db domain.IUser) {
r, err := db.CreateUserTable()
if err != nil {
fmt.Println("error", err)
}
fmt.Println(r)
}
I've implemented the interface somewhere else in the code, because the program just work as the implemented interface expected to be.
IUser is an interface whose member is:
type IUser interface {
CreateUserTable() (sql.Result, error)
}
InitDB is a function to open the database and return struct of database:
type DB struct {
*sql.DB
}
//InitDB initializes the database
func InitDB() *DB {
db, err := sql.Open(dbDriver, dbName)
if err != nil {
log.Fatal("failed to initialize database: ",err)
}
err2 := db.Ping()
if err2 != nil {
log.Fatal(err2)
}
return &DB{db}
}
My question is: how can a function with a parameter type interface be passed a different type of parameter? And how is this working under the hood?

As per Golang Spec
An interface type specifies a method set called its interface. A
variable of interface type can store a value of any type with a method
set that is any superset of the interface. Such a type is said to
implement the interface.
This is because interface can be implemented as a wrapper to every type. Interface actually points to two things mainly one is the underlying type which is a struct here and other one is the value of that type which is a pointer to DB
You see newFunc is actually taking interface{} as an argument, So you can pass anything to it of type T which can be of primitive types too.
func main() {
db := model.InitDB()
newFunc(db)
}
So In case you want to get the underlying value you need to type assert. Interface works like a wrapper to the struct here and save its type and value which can be get using type assertion.

Related

Is this example of method overriding in Go?

package main
import (
"encoding/json"
"fmt"
"log"
"strings"
)
type Animal int
const (
Unknown Animal = iota
Gopher
Zebra
)
func (a *Animal) UnmarshalJSON(b []byte) error {
var s string
if err := json.Unmarshal(b, &s); err != nil {
return err
}
switch strings.ToLower(s) {
default:
*a = Unknown
case "gopher":
*a = Gopher
case "zebra":
*a = Zebra
}
return nil
}
func (a Animal) MarshalJSON() ([]byte, error) {
var s string
switch a {
default:
s = "unknown"
case Gopher:
s = "gopher"
case Zebra:
s = "zebra"
}
return json.Marshal(s)
}
func main() {
blob := `["gopher","armadillo","zebra","unknown","gopher","bee","gopher","zebra"]`
var zoo []Animal
if err := json.Unmarshal([]byte(blob), &zoo); err != nil {
log.Fatal(err)
}
census := make(map[Animal]int)
for _, animal := range zoo {
census[animal] += 1
}
fmt.Printf("Zoo Census:\n* Gophers: %d\n* Zebras: %d\n* Unknown: %d\n",
census[Gopher], census[Zebra], census[Unknown])
}
This is the code snippet of Json custom marshal example in go doc. My question is where is the call to MarshalJSON and UnmarshalJSON method in this code. Are these method somehow overriding Json package's UnmarshalJSON and MarshalJSON method. I thought go does not support method overriding this way. Pls help, i am not able to understand what is happening in this code!!
The documentation says:
To unmarshal JSON into a value implementing the Unmarshaler interface, Unmarshal calls that value's UnmarshalJSON method, including when the input is a JSON null.
Somewhere in the json.Unmarshal implementation, there's code similar this:
u, ok := v.(Unmarshaler)
if ok {
err := u.Unmarshal(data)
if err != nil { /* handle error */}
} else {
// handle other kinds of values
}
The code uses a type assertion to determine if the value satisfies the json.Unmarshaler interface. If the value does satisfy the method, the value's UnmarshalJSON function is called.
The (*Animal).UnmarshalJSON function is called because *Animal satisfies the json.Unmarshaler interface.
This is an example of implementing an interface from a different package.
There's no need to explicitly declare that you're implementing an interface in Go, like there is in Java or C++ for example. You just have to implement all the functions it declares. In this case, you're implementing the Unmarshaler interface declared in the json package which is used by the Unmarshal function.

How to type-switch values with generic types [duplicate]

Just started learning generics. I'm making a command processor and I honestly don't know how to word this so I'm just going to show an example problem:
var ErrInvalidCommand = errors.New("invalid command")
type TransactionalFn[T any] func(ctx context.Context, db T) error
func NewTransactionalCommand[T any](fn TransactionalFn[T]) *TransactionalCommand[T] {
return &TransactionalCommand[T]{
fn: fn,
}
}
type TransactionalCommand[T any] struct {
fn TransactionalFn[T]
}
func (cmd *TransactionalCommand[T]) StartTransaction() error {
return nil
}
func (cmd *TransactionalCommand[T]) Commit() error {
return nil
}
func (cmd *TransactionalCommand[T]) Rollback() error {
return nil
}
type CMD interface{}
type CommandManager struct{}
func (m *CommandManager) Handle(ctx context.Context, cmd CMD) error {
switch t := cmd.(type) {
case *TransactionalCommand[any]:
return m.handleTransactionalCommand(ctx, t)
default:
fmt.Printf("%T\n", cmd)
return ErrInvalidCommand
}
}
func (m *CommandManager) handleTransactionalCommand(ctx context.Context, cmd *TransactionalCommand[any]) error {
if err := cmd.StartTransaction(); err != nil {
return err
}
if err := cmd.fn(ctx, nil); err != nil {
if err := cmd.Rollback(); err != nil {
return err
}
}
if err := cmd.Commit(); err != nil {
return err
}
return nil
}
// tests
type db struct{}
func (*db) Do() {
fmt.Println("doing stuff")
}
func TestCMD(t *testing.T) {
ctx := context.Background()
fn := func(ctx context.Context, db *db) error {
fmt.Println("test cmd")
db.Do()
return nil
}
tFn := bus.NewTransactionalCommand(fn)
mng := &bus.CommandManager{}
err := mng.Handle(ctx, tFn)
if err != nil {
t.Fatal(err)
}
}
mng.handle returns ErrInvalidCommand so the test fails because cmd is *TransactionalCommand[*db] and not *TransactionalCommand[any]
Let me give another, more abstract example:
type A[T any] struct{}
func (*A[T]) DoA() { fmt.Println("do A") }
type B[T any] struct{}
func (*B[T]) DoB() { fmt.Println("do B") }
func Handle(s interface{}) {
switch x := s.(type) {
case *A[any]:
x.DoA()
case *B[any]:
x.DoB()
default:
fmt.Printf("%T\n", s)
}
}
func TestFuncSwitch(t *testing.T) {
i := &A[int]{}
Handle(i) // expected to print "do A"
}
Why doesn't this switch statement case *A[any] match *A[int]?
How to make CommandManager.Handle(...) accept generic Commands?
Why does the generic type switch fail to compile?
This is in fact the result of an intentional decision of the Go team. It turned out that allowing type switches on parametrized types can cause confusion
In an earlier version of this design, we permitted using type assertions and type switches on variables whose type was a type parameter, or whose type was based on a type parameter. We removed this facility because it is always possible to convert a value of any type to the empty interface type, and then use a type assertion or type switch on that. Also, it was sometimes confusing that in a constraint with a type set that uses approximation elements, a type assertion or type switch would use the actual type argument, not the underlying type of the type argument (the difference is explained in the section on identifying the matched predeclared type)
From the Type Parameters Proposal
Let me turn the emphasized statement into code. If the type constraint uses type approximation (note the tildes)...
func PrintStringOrInt[T ~string | ~int](v T)
...and if there also was a custom type with int as the underlying type...
type Seconds int
...and if PrintOrString() is called with a Seconds parameter...
PrintStringOrInt(Seconds(42))
...then the switch block would not enter the int case but go right into the default case, because Seconds is not an int. Developers might expect that case int: matches the type Seconds as well.
To allow a case statement to match both Seconds and int would require a new syntax, like, for example,
case ~int:
As of this writing, the discussion is still open, and maybe it will result in an entirely new option for switching on a type parameter (such as, switch type T).
More details, please refer to proposal: spec: generics: type switch on parametric types
Trick: convert the type into 'any'
Luckily, we do not need to wait for this proposal to get implemented in a future release. There is a super simple workaround available right now.
Instead of switching on v.(type), switch on any(v).(type).
switch any(v).(type) {
...
This trick converts v into an empty interface{} (a.k.a. any), for which the switch happily does the type matching.
Source: A tip and a trick when working with generics
*A[any] does not match *A[int] because any is a static type, not a wildcard. Therefore instantiating a generic struct with different types yields different types.
In order to correctly match a generic struct in a type switch, you must instantiate it with a type parameter:
func Handle[T any](s interface{}) {
switch x := s.(type) {
case *A[T]:
x.DoA()
case *B[T]:
x.DoB()
default:
panic("no match")
}
}
Though in absence of other function arguments to infer T, you will have to call Handle with explicit instantiation. T won't be inferred from the struct alone.
func main() {
i := &A[int]{}
Handle[int](i) // expected to print "do A"
}
Playground: https://go.dev/play/p/2e5E9LSWPmk
However when Handle is actually a method, as in your database code, this has the drawback of choosing the type parameter when instantiating the receiver.
In order to improve the code here you can make Handle a top-level function:
func Handle[T any](ctx context.Context, cmd CMD) error {
switch t := cmd.(type) {
case *TransactionalCommand[T]:
return handleTransactionalCommand(ctx, t)
default:
fmt.Printf("%T\n", cmd)
return ErrInvalidCommand
}
}
Then you have the problem of how to supply the argument db T to the command function. For this, you might:
simply pass an additional *db argument to Handle and handleTransactionalCommand, which also helps with type parameter inference. Call as Handle(ctx, &db{}, tFn). Playground: https://go.dev/play/p/6WESb86KN5D
pass an instance of CommandManager (like solution above but *db is wrapped). Much more verbose, as it requires explicit instantiation everywhere. Playground: https://go.dev/play/p/SpXczsUM5aW
use a parametrized interface instead (like below). So you don't even have to type-switch. Playground: https://go.dev/play/p/EgULEIL6AV5
type CMD[T any] interface {
Exec(ctx context.Context, db T) error
}

Function that accepts generic struct

Is it possible to have my function definition below accept any type of struct?
I've tried to refactor like so:
// This method should accept any type of struct
// Once I receive my response from the database,
// I scan the rows to create a slice of type struct.
func generateResponse(rows *sqlx.Rows, structSlice []struct{}, structBody struct{}) ([]struct{}, error) {
for rows.Next() {
err := rows.StructScan(&structBody)
if err != nil {
return nil, err
}
structSlice = append(structSlice, structBody)
}
err := rows.Err()
if err != nil {
return nil, err
}
return structSlice, nil
}
Assume my struct is of type OrderRevenue.
When I call the function above:
structSlice, err := generateResponse(rows, []OrderRevenue{}, OrderRevenue{})
The error I get is:
cannot use []OrderRevenue literal as type []struct{} in argument...
Am I going about this the wrong way?
This is considered the cornerstone (or more of a limitation) of Go's type system. struct{} is an unnamed type that is different from struct{ field1 int } and of course is not the same as OrderRevenue{}.
Go emphasizes abstraction through interfaces, and perhaps you should try that. Here is the first take:
type OrderRevenue interface {
MarshalMyself() ([]byte, error)
}
type Anonymous struct {}
func (a Anonymous) MarshalMyself() ([]byte, error) {
// implementation's up to you
return []byte{}, nil
}
// the function signature
generateResponse(rows *sqlx.Rows, structSlice []OrderRevenue, structBody Body) ([]Body, error) {
// ...
}
In this case you can also use empty interface interface{}, which all types implement, but you'll have to recursively go through the structure to do manual type assertion. The best approach in Go is to know the shape of your data in advance, at least partially.

Error when trying to inject dependency

I'm trying refactor some code to use dependency injection for the Docker client library I use in my code. I created an interface with the method I want to be able to mock
type DockerClient interface {
Ping(context.Context) (types.Ping, error)
}
func NewDockerUtil() (*DockerUtil, error) {
var dockerClient *DockerClient
var err error
dockerClient, err = client.NewEnvClient() //Reports incompatible types in binary and unary expressions.
if err != nil {
return nil, err
}
return &DockerUtil{
Client: dockerClient,
}, nil
}
type DockerUtil struct{
Client *DockerClient
}
But when I try to assign it I get Reports incompatible types in binary and unary expressions. What exactly do I need to change?
Let's start with using interface{}, when you define interface don't use pointer definition, good read here.
type DockerUtil struct{
Client DockerClient
}
And moby Client implements lot of methods and you would like to do interface for selective methods.
Right way to do is via Type assertion. Good read Effective Go - Type assertions and Spec - Type assertion.
Note: try this code, I don't have docker env in my machine to test.
func NewDockerUtil() (*DockerUtil, error) {
dockerClient, err := client.NewEnvClient()
if err != nil {
return nil, err
}
return &DockerUtil{
Client: dockerClient.(DockerClient),
}, nil
}
Note:
Using DockerUtil.Client, you can call only Ping method since your interface DockerClient has definition of Ping method.
If you would like to call all the methods supported by client.Client later on then you have to do type assertion-
dockerClient := DockerUtil.Client.(*client.Client)

Extend struct in Go

Suppose object A has a field of type net.Dialer. I'd like to provide object A with a custom implementation of net.Dialer that augments the Dial method. Is this doable in Go? I'm trying to use embedded fields like so:
package main
import (
"net"
"fmt"
)
type dialerConsumer struct {
dialer net.Dialer
}
func (dc *dialerConsumer) use() error {
conn, e := dc.dialer.Dial("tcp", "golang.org:http")
if e != nil {
return e
}
fmt.Printf("conn: %s\n", conn)
return nil
}
type customDialer struct {
net.Dialer
}
func main() {
standardDialer := net.Dialer{}
consumer := &dialerConsumer{
dialer: standardDialer,
}
consumer.use()
/*
customDialer := customDialer{
net.Dialer{},
}
consumer = &dialerConsumer{
dialer: customDialer,
}
consumer.use()
*/
}
However, when I uncomment the commented-out code in main, I get the following compilation error:
src/test.go:38: cannot use customDialer (type customDialer) as type net.Dialer in field value
You're getting the error because customDialer and net.Dialer are two different types and cannot be used interchangeably. Embedding in Go is not the same as class inheritance in other OO langauges so it won't help you with what you trying to do.
What you can do instead in this case is to use Go interfaces which give you something like polymorphism/duck-typing, and since interfaces in Go are satified implicitly you can define a new interface that an existing type will implement by virtue of having a method with the same signature as the newly defined interface.
// already implemented by net.Dialer
type Dialer interface {
Dial(network, address string) (net.Conn, error)
}
type customDialer struct {
*net.Dialer
}
func (cd *customDialer) Dial(network, address string) (net.Conn, error) {
conn, err := cd.Dialer.Dial(network, address)
if err != nil {
return nil, err
}
fmt.Printf("conn: %s\n", conn)
return conn, nil
}
// now the dialer field can be set to *customDialer and net.Dialer as well
type dialerConsumer struct {
dialer Dialer
}
https://play.golang.org/p/i3Vpsh3wii

Resources