I want to use slacknotificationprovider in the NewNotifier function. How can I do it. Also I want send a string(config.Cfg.SlackWebHookURL) in newNotifier function. What should I do? Also please suggest me some material to get a deeper knowledge of struct and interface in golang.
I also want to know why ProviderType.Slack is not defined as I have mentioned it in ProviderType struct as of SlackNotificationProvider type? Thanks.
type SlackNotificationProvider struct {
SlackWebHookURL string
PostPayload PostPayload
}
type ProviderType struct {
Slack SlackNotificationProvider
Discord DiscordNotificationProvider
}
type Notifier interface {
SendNotification() error
}
func NewNotifier(providerType ProviderType) Notifier {
if providerType == ProviderType.Slack {
return SlackNotificationProvider{
SlackWebHookURL: SlackWebHookURL,
}
} else if providerType == ProviderType.Discord {
return DiscordNotificationProvider{
DiscordWebHookURL: SlackWebHookURL + "/slack",
}
}
return nil
}
slackNotifier := NewNotifier(config.Cfg.SlackWebHookURL)
Errors:
1. cannot use config.Cfg.SlackWebHookURL (type string) as type ProviderType in argument to NewNotifiergo
2. ProviderType.Slack undefined (type ProviderType has no method Slack)go
Golang is a strongly typed language, which means the arguments to your functions are defined and can't be different. Strings are strings and only strings, structs are structs and only structs. Interfaces are golang's way of saying "This can be any struct that has method(s) with the following signatures". So you can't pass a string as a ProviderType and none of your structs actually implement the interface method you've defined, so nothing would work as you've laid it out. To reorganize what you've got into something that might work:
const (
discordType = "discord"
slackType = "slack"
)
// This means this will match any struct that defines a method of
// SendNotification that takes no arguments and returns an error
type Notifier interface {
SendNotification() error
}
type SlackNotificationProvider struct {
WebHookURL string
}
// Adding this method means that it now matches the Notifier interface
func (s *SlackNotificationProvider) SendNotification() error {
// Do the work for slack here
}
type DiscordNotificationProvider struct {
WebHookURL string
}
// Adding this method means that it now matches the Notifier interface
func (s *DiscordNotificationProvider) SendNotification() error {
// Do the work for discord here
}
func NewNotifier(uri, typ string) Notifier {
switch typ {
case slackType:
return SlackNotificationProvider{
WebHookURL: uri,
}
case discordType:
return DiscordNotificationProvider{
WebHookURL: uri + "/slack",
}
}
return nil
}
// you'll need some way to figure out what type this is
// could be a parser or something, or you could just pass it
uri := config.Cfg.SlackWebHookURL
typ := getTypeOfWebhook(uri)
slackNotifier := NewNotifier(uri, typ)
As far as documentation to help with this, the "Go By Example" stuff is good, and I see other people have already linked that. That said, a struct with one method feels like it should be a function, which you can also define as a type to allow you to pass back a few things. Example:
type Foo func(string) string
func printer(f Foo, s string) {
fmt.Println(f(s))
}
func fnUpper(s string) string {
return strings.ToUpper(s)
}
func fnLower(s string) string {
return strings.ToLower(s)
}
func main() {
printer(fnUpper, "foo")
printer(fnLower, "BAR")
}
Related
I have two different interfaces (from two different packages) that I want to implement. But they conflict, like this:
type InterfaceA interface {
Init()
}
type InterfaceB interface {
Init(name string)
}
type Implementer struct {} // Wants to implement A and B
func (i Implementer) Init() {}
func (i Implementer) Init(name string) {} // Compiler complains
It says "Method redeclared". How can one struct implement both interfaces?
As already answered, this is not possible since Golang does not (and probably will not) support method overloading.
Look at Golang FAQ:
Experience with other languages told us that having a variety of methods with the same name but different signatures was occasionally useful but that it could also be confusing and fragile in practice. Matching only by name and requiring consistency in the types was a major simplifying decision in Go's type system.
It is not possible.
In go you must have a single method signature.
You should rename one method.
The method signatures must match. If you want dependency injection I would recommend the functional option pattern. Functional options are functions that return other functions that are called in a loop in the constructor. Here is an example of how to use functional options and the basics of interfaces in go.
package main
import (
"fmt"
"strconv"
)
type SomeData struct {
data string
}
// SomeData and SomeOtherData both implement SomeInterface and SomeOtherInterface
// SomeInterface and SomeOtherInterface both implement each other.
type SomeInterface interface {
String() string
Set(data string)
}
func (s *SomeData)String() string {
return s.data
}
func (s *SomeData)Set(data string) {
s.data = data
}
// SetDataOption is a functional option that can be used to inject a constructor dep
func SetDataOption(data string) func(*SomeData) {
return func(s *SomeData) {
s.Set(data)
}
}
// NewSomeData is the constructor; it takes in 0 to many functional options and calls each one in a loop.
func NewSomeData(options ...func(s *SomeData)) SomeInterface {
s := new(SomeData)
for _, o := range options {
o(s)
}
return s
}
//********************
type SomeOtherData struct {
data string
i int
}
type SomeOtherInterface interface {
String() string
Set(data string)
}
func (s *SomeOtherData)String() string {
return s.data + " " + strconv.Itoa(s.i)
}
func (s *SomeOtherData)Set(data string) {
s.data = data
}
func SetOtherDataOption(data string) func(*SomeOtherData) {
return func(s *SomeOtherData) {
s.Set(data)
}
}
func SetOtherIntOption(i int) func(*SomeOtherData) {
return func(s *SomeOtherData) {
s.i = i
}
}
// NewSomeOther data works just like NewSomeData only in this case, there are more options to choose from
// you can use none or any of them.
func NewSomeOtherData(options ...func(s *SomeOtherData)) SomeOtherInterface {
s := new(SomeOtherData)
for _, o := range options {
o(s)
}
return s
}
//*********************************
// HandleData accepts an interface
// Regardless of which underlying struct is in the interface, this function will handle
// either by calling the methods on the underlying struct.
func HandleData(si SomeInterface) {
fmt.Println(si) // fmt.Println calls the String() method of your struct if it has one using the Stringer interface
}
func main() {
someData := NewSomeData(SetDataOption("Optional constructor dep"))
someOtherData := NewSomeOtherData(SetOtherDataOption("Other optional constructor dep"), SetOtherIntOption(42))
HandleData(someData) // calls SomeData.String()
HandleData(someOtherData) // calls SomeOtherData.String()
someOtherData = someData // assign the first interface to the second, this works because they both implement each other.
HandleData(someOtherData) // calls SomeData.String() because there is a SomeData in the someOtherData variable.
}
I am playing around with Go and found a problem I can't get around. Suppose I have my code like this:
// Imagine this is an external package for querying MySQL: I run a query
// and it gives me back a struct with a method "Result" to get the result
// as a string
// I can NOT modify this code, since it is an external package
package bar
type MySQL struct {}
func (m *MySQL) RunQuery() *MySQLResult {
return &MySQLResult{}
}
type MySQLResult struct {}
func (r *MySQLResult) Result() string {
return "foo"
}
I imported the package and started to use it:
// I created a little runner to help me
func run(m *bar.MySQL) string {
return m.RunQuery().Result()
}
func main() {
m := &bar.MySQL{}
fmt.Println(run(m)) // Prints "foo"
}
I really like my helper "run", but I'd like to make it more generous: I don't expect people to always pass me a MySQL client. It could be anything that has a "RunQuery" and "Result" method. So I try to use interfaces:
type AnyDB interface {
RunQuery() interface{ Result() string }
}
func run(m AnyDB) string {
return m.RunQuery().Result()
}
Sadly, this doesn't compile anymore. I get this error:
cannot use m (type *MySQL) as type AnyDB in argument to run:
*MySQL does not implement AnyDB (wrong type for RunQuery method)
have RunQuery() *MySQLResult
want RunQuery() interface { Result() string }
Is this not supported by Go, or am I doing something wrong?
RunQuery should return interface, otherwise you always have to deal with strong type.
AnyDB is not required, I added it for connivence.
AnyResult should be defined in bar package or being imported into it.
type AnyDB interface {
RunQuery() AnyResult
}
type MySQL struct{}
func (m *MySQL) RunQuery() AnyResult {
return &MySQLResult{}
}
type AnyResult interface {
Result() string
}
type MySQLResult struct{}
func (r *MySQLResult) Result() string {
return "foo"
}
Foreign application API gives me a list of names in JSON format. I need modify all of those.
But I do not like to write some loop for it (especially after Python using with reflection and stuff)
Is there any method to write something like this in Go?
type MyIncredibleType struct {
Name ModifyName // ModifyName is not a type!
}
func ModifyName(input string) string {
return input + ".com"
}
The expected behavior of this is:
a := MyIncredibleType{Name: "Abracadabra"}
print(a.Name) // Abracadabra.com
This seems pretty straight forward to me, assuming I understand your question correctly:
// ModifyName func
func ModifyName(input string) string {
return fmt.Sprintf("%v.com", input)
}
If you wish to achieve this within the type itself, without modifying (mutating) the internal state:
type MyType sturct {
name string // unexported
}
// accessor func to return name
func (t MyType) Name() string {
return t.name
}
// accessor func to return modified name
func (t MyType) ModifiedName() string {
return fmt.Sprintf("%v.com", t.name)
}
If you want to modify the internal state:
type MyType struct {
name string
}
// mutator func (note the pointer for pass by reference)
func (t *MyType) ModifyName(input string) {
t.name = fmt.Sprintf("%v.com", input)
}
// accessor (note no pointer for pass by value)
func (t MyType) Name() string {
return t.name
}
This is is not possible in GO. That's not how struct works in Go.
type MyIncredibleType struct {
Name ModifyName `json:"name"` // ModifyName is not a type!
}
you can only define Built-in types for your fields of struct or you can define Composite Literal types.
Composite literals construct values for structs, arrays, slices, and
maps and create a new value each time they are evaluated. They consist
of the type of the literal followed by a brace-bound list of elements.
Each element may optionally be preceded by a corresponding key.
Try to create a method receiver of struct which you are using to parse json coming from the api to modify the name. That will let you achieve something similar to what you want.
package main
import (
"fmt"
)
type MyIncredibleType struct {
Name string `json:"name"` // ModifyName is not a type!
}
func(myIncredibleType *MyIncredibleType) ModifyName() string {
return myIncredibleType.Name+".com"
}
func main() {
a := MyIncredibleType{Name: "Abracadabra"}
name := a.ModifyName()
fmt.Printf("%s",name)
}
Playground Example
Or you can pass an interface which will wrap any struct value with name field and then use Type assertion to get the underlying value to modify the same and return the result:
package main
import (
"fmt"
)
type MyIncredibleType struct {
Name string `json:"name"` // ModifyName is not a type!
}
func ModifyName(input interface{}) string{
return input.(interface{}).(string)+".com"
}
func main() {
a := MyIncredibleType{Name: "Abracadabra"}
name := ModifyName(a.Name)
fmt.Printf("%s",name)
}
Working code on Go Playground
For more information also go through Golang method Declarations on how to create receivers.
I have the following code:
package vault
type Client interface {
GetHealth() error
}
func (c DefaultClient) GetHealth () error {
resp := &VaultHealthResponse{}
err := c.get(resp, "/v1/sys/health")
if err != nil {
return err
}
return nil;
}
Now, I want to use this function as part of this struct:
type DependencyHealthFunction func() error
type Dependency struct {
Name string `json:"name"`
Required bool `json:"required"`
Healthy bool `json:"healthy"`
Error error `json:"error,omitempty"`
HealthFunction DependencyHealthFunction
}
Basically, set the value of HealthFunction to GetHealth. Now, when I do the following:
func (config *Config) GetDependencies() *health.Dependency {
vaultDependency := health.Dependency{
Name: "Vault",
Required: true,
Healthy: true,
HealthFunction: vault.Client.GetHealth,
}
temp1 := &vaultDependency
return temp1;
}
This gives me an error and it says cannot use vault.Client.GetHealth (type func(vault.Client) error) as type health.DependencyHealthFunction in field value. How can I do this?
Edit: How DependencyHealthFunction is used?
As its part of Dependency struct, it's simply used as following: d.HealthFunction() where d is a variable of type *Dependency.
This is abstract:
HealthFunction: vault.Client.GetHealth,
If we were to call HealthFunction(), what code do you expect to run? vault.Client.GetHealth is just a promise that such a function exists; it isn't a function itself. Client is just an interface.
You need to create something that conforms to Client and pass its GetHealth. For example, if you had a existing DefaultClient such as:
defaultClient := DefaultClient{}
Then you could pass its function:
HealthFunction: defaultClient.GetHealth,
Now when you later call HealthFunction() it will be the same as calling defaultClient.GetHealth().
https://play.golang.org/p/9Lw7uc0GaE
I believe the issue is related to understanding how interfaces are treated in Go.
An interface simply defines a method or set of methods that a particular type must satisfy to be considered as "implementing" the interface.
For example:
import "fmt"
type Greeter interface {
SayHello() string
}
type EnglishGreeter struct{}
// Satisfaction of SayHello method
func (eg *EnglishGreeter) SayHello() string {
return "Hello"
}
type SpanishGreeter struct{}
func (sg *SpanishGreeter) SayHello() string {
return "Ola"
}
func GreetPerson(g Greeter) {
fmt.Println(g.SayHello())
}
func main() {
eg := &EnglishGreeter{}
sg := &SpanishGreeter{}
// greet person in english
GreetPerson(eg)
// greet person in spanish
GreetPerson(sg)
}
You can add this behavior into a custom struct by simply having a Greeter field inside the struct. ie
type FrontEntrance struct {
EntranceGreeter Greeter
}
fe := &FrontEntrance { EntranceGreeter: &EnglishGreeter{} }
// then call the SayHello() method like this
fe.EntranceGreeter.SayHello()
Interfaces in golang are useful for composing common expected behavior for types based on the methods that they satisfy.
Hope this helps.
I have the following code:
package main
import (
"log"
)
type Data struct {
Id int
Name string
}
type DataError struct {
Message string
ErrorCode string
}
func main() {
response := Data{Id: 100, Name: `Name`}
if true {
response = DataError{Message: `message`, ErrorCode: `code`}
}
log.Println(response)
}
This code returns me an error:
./start.go:20: cannot use DataError literal (type DataError) as type
Data in assignment
Seems to be that I could not assign to response var data with different type (in my case DataError). I heard that possible solution could be to unite Data and DataError structs via interface. Or maybe there is another better solution?
Could you please point me how to resolve this problem?
Thanks
It looks like you're trying to make a union type (what the ML-family of languages calls "enum"). I know of a couple of patterns for this:
0. Basic error handling (playground)
I suspect what you're doing is just basic error handling. In Go, we use multiple return values and check the result. This is almost certainly what you want to do:
package main
import (
"fmt"
"log"
)
type Data struct {
ID int
Name string
}
type DataError struct {
Message string
ErrorCode string
}
// Implement the `error` interface. `error` is an interface with
// a single `Error() string` method
func (err DataError) Error() string {
return fmt.Sprintf("%s: %s", err.ErrorCode, err.Message)
}
func SomeFunction(returnData bool) (Data, error) {
if returnData {
return Data{ID: 42, Name: "Answer"}, nil
}
return Data{}, DataError{
Message: "A thing happened",
ErrorCode: "Oops!",
}
}
func main() {
// this bool argument controls whether or not an error is returned
data, err := SomeFunction(false)
if err != nil {
log.Fatalln(err)
}
fmt.Println(data)
}
1. Interfaces (playground)
Again, if your options are good-data and error, you should probably use the first case (stick with the idiom/convention), but other times you might have multiple "good data" options. We can use interfaces to solve this problem. Here we're adding a dummy method to tell the compiler to constrain the possible types that can implement this interface to those that have an IsResult() method. The biggest downside to this is that sticking things into an interface can incur an allocation, which can be detrimental in a tight loop. This pattern isn't terribly common.
package main
import "fmt"
type Result interface {
// a dummy method to limit the possible types that can
// satisfy this interface
IsResult()
}
type Data struct {
ID int
Name string
}
func (d Data) IsResult() {}
type DataError struct {
Message string
ErrorCode string
}
func (err DataError) IsResult() {}
func SomeFunction(isGoodData bool) Result {
if isGoodData {
return Data{ID: 42, Name: "answer"}
}
return DataError{Message: "A thing happened", ErrorCode: "Oops!"}
}
func main() {
fmt.Println(SomeFunction(true))
}
2. Tagged Union (playground)
This case is similar to the previous case, except instead of using an interface, we're using a struct with a tag that tells us which type of data the struct contains (this is similar to a tagged union in C, except the size of the struct is the sum of its potential types instead of the size of its largest potential type). While this takes up more space, it can easily be stack-allocated, thus making it tight-loop friendly (I've used this technique to reduce allocs from O(n) to O(1)). In this case, our tag is a bool because we only have two possible types (Data and DataError), but you could also use a C-like enum.
package main
import (
"fmt"
)
type Data struct {
ID int
Name string
}
type DataError struct {
Message string
ErrorCode string
}
type Result struct {
IsGoodData bool
Data Data
Error DataError
}
// Implements the `fmt.Stringer` interface; this is automatically
// detected and invoked by fmt.Println() and friends
func (r Result) String() string {
if r.IsGoodData {
return fmt.Sprint(r.Data)
}
return fmt.Sprint(r.Error)
}
func SomeFunction(isGoodData bool) Result {
if isGoodData {
return Result{
IsGoodData: true,
Data: Data{ID: 42, Name: "Answer"},
}
}
return Result{
IsGoodData: false,
Error: DataError{
Message: "A thing happened",
ErrorCode: "Oops!",
},
}
}
func main() {
// this bool argument controls whether or not an error is returned
fmt.Println(SomeFunction(true))
}
You can't assign 2 different types that are not "assignable" to the same variable ... unless you use a specific interface signature or empty interface.
https://golang.org/ref/spec#Assignability
that code would compile :
func main() {
var response interface{} // empty interface AKA Object AKA void pointer
response = Data{Id: 100, Name: `Name`}
if true {
response = DataError{Message: `message`, ErrorCode: `code`}
}
log.Println(response)
}
since every type implements empty interface, but you want to do that only if there is no other options.
if 2 types share some methods use a specific interface, for instance (pseudo-code) :
type Responder interface {
Respond() string
}
type Data struct { /* code */
}
func (d Data) Respond() string { return "" }
type DataError struct { /* code */
}
func (d DataError) Respond() string { return "" }
func main() {
var response Responder // declared as interface
response = Data{}
response = DataError{}
fmt.Println(response)
}
Whenever you have doubts a quick scan of the go spec is useful, it is the only authority and pretty well written compared to most specs out there. For the most part the rules are crystal clear, and that's a strength of Go.