Trying to teach myself some Go by following an online course. And I'm trying to go a bit off course to expand on my learning a bit.
The course had us writing a simple function using a couple variables and the function would take the two variables and print out a line. So I had:
func main() {
var greeting := "hello"
var name := "cleveland"
message := printMessage(greeting,name)
fmt.Println(message)
}
func printMessage(greeting string, name string) (message string) {
return greeting + " " + name + "!"
}
Later the course introduced a way to create an pseudo-array of strings using the using the
func sayHello (cities ...string) (message string) {
for _, city := range cities {
message := printMessage("hello", city)
fmt.Println(message)
}
}
I would like to create a struct with different greetings and pass those into the sayHello function. So the struct and the variables would looks something like this:
type cityInfo struct {
greeting string
name string
wins float32
gamesPlayed float32
}
city1 := cityInfo{"hello", "cleveland"}
city2 := cityInfo{"good morning", "atlanta"}
...and so on
How do I format the function to pass those structs into the function so that I can iterate on the number of structs and get the greetings and names using city.greeting and city.name? Does this question make sense?
Function argument type can be any valid type:
func sayHello (cities ...cityInfo) {
for _, city := range cities {
message := printMessage(city.greeting, city.name)
fmt.Println(message)
}
}
One solution would be to create an interface and a greeting method.
For example:
type Greetable interface {
Greeting() string
Name() string
}
You would then implement the Greeting and Name methods in your struct (this would immediately implement the Greetable interface, due to the way that Go handles interfaces):
type cityInfo struct {
name string
greeting string
}
func (city *cityInfo) Greeting() string {
return city.greeting
}
func (city *cityInfo) Name() string {
return city.name
}
And then your function would just accept anything that implements Greetable:
func sayHello(greetables ...Greetable) (message string)
And use the Name() and Greeting() methods instead.
Related
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")
}
In this playground link I have created a contrived version of my code where I am creating a variable of type X based on an input string. The variable will be one of a handful of types and implement an interface.
The code currently compiles and provides the correct result, however it strikes me as quite verbose and I'm trying to find if there is a shorthand approach to the result I'm achieving. The example has 3 types (dog, cat & bird) that implement the interface (animal), however my actual code will have up to 40 types in this switch statement.
The reason I am using this code is when retrieving results form a DBMS, I'm trying to use a generic load method that, when combined with sqlx, loads the database table into the correct struct based on the input string. I have entire control over the application and can change the input string to another type if required.
Code from playground link:
package main
import (
"fmt"
)
type animal interface {
call() string
}
type dog struct {
}
func (d *dog) call() string {
return "Woof!"
}
type cat struct {
}
func (c *cat) call() string {
return "Meow!"
}
type bird struct {
}
func (c *bird) call() string {
return "Chirp!"
}
func main() {
var animal animal
animalType := "dog"
switch animalType{
case "dog":
animal = new(dog)
case "cat":
animal = new(cat)
case "bird":
animal = new(bird)
You can create a hashmap from "string" to "function that returns animal" but setting that up would be more verbose than the switch statement.
Something like this (not tested)
type AnimalCtor func() animal
var animalMap map[string]AnimalCtor
.....
func init() {
animalMap["dog"] = func() animal { return &dog{} }
animalMap["cat"] = func() animal { return &cat{} }
animalMap["bird"] = func() animal { return &bird{} }
.....
}
func createAnimalFromString(input string) animal {
ctor, ok := animalMap[input]
if ok {
return ctor()
} else {
return nil
}
}
But it's a lot more verbose than the switch statement and obscures what should otherwise be explicit and clear.
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.
package main
import (
"fmt"
)
type animal interface {
speak()
}
type dog struct {
name, sound string
}
type cat struct {
name, sound string
}
func (d dog) speak() {
fmt.Println(d.name, " goes ", d.sound)
}
func (c cat) speak() {
fmt.Println(c.name, " goes ", c.sound)
}
func animal_speak(a animal) {
fmt.Println(a.speak())
}
func main() {
dogo := dog{"scooby", "woof"}
cato := cat{"garfield", "meow"}
animal_speak(dogo)
animal_speak(cato)
}
When I call the animal interface it gives me the following error
./interface.go:28:21: a.speak() used as value
What am I doing wrong?
Link to playground
The interface is not used as a value. You're using a function call that returns nothing as a value.
speak() returns nothing... so what do you expect it to print?
Since you are printing the output of speak method, your speak method needs to return a string or an object whose string representation would print a string you would like to see. Here's your program modified https://play.golang.org/p/VDsp0cjXBd- to return a string.
This answer is based on all above answers,
Modified below function
Before
func animal_speak(a animal) {
fmt.Println(a.speak())
}
After
func animal_speak(a animal) {
a.speak()
}
Playgorund Link
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.