Is it possible to do conditional variable type declaration like this in Golang?
if isAdmin {
var result NormalResult
} else {
var result AdminResult
}
// do something to &result
doSomething(&result)
func doSomething(interface{}) {
// something
}
The above does not work, but the ideas is that normalResult and adminResults are very similar structs and how would I go about doing this?
Thank you!
No, not in this manner. Go being statically typed, needs to know the type information at compile time.
What you could do is declare result as an interface of some type which both AdminResult and NormalResult satisfy. You can then use a type assertion at runtime to decide which type of result it is.
(You also have to declare result outside of the if blocks because Go is block scoped)
type NormalResult struct {
Value int
}
func (r NormalResult) Result() int {
return r.Value
}
type AdminResult struct {
Value int
}
func (r AdminResult) Result() int {
return r.Value
}
type Resulter interface {
Result() int
}
func main() {
isAdmin := true
var r Resulter
if isAdmin {
r = AdminResult{2}
} else {
r = NormalResult{1}
}
fmt.Println("Hello, playground", r)
}
Depending on what kind of similarities, you might have different options.
Using embedded structs
Depending on your structure, you might be able to use embedded structs. Let's say NormalResult is defined like this:
type NormalResult struct {
Name string
Value int
}
And if AdminResult shares the same properties but just adds a few more of them (like UserId), you can choose to embed NormalResult into the AdminResult like this:
type AdminResult struct {
*NormalResult
UserId int64
}
Then you can also declare methods for NormalResult which will be promoted to AdminResult as well:
func (r *NormalResult) doSomething() {
// Doing something
}
Edit
And, no, it is not possible to have conditional types in Go as you suggested. A variable can only be of one type, be it NormalResult, AdminResult or interface{}
Related
Assuming we have the following structures:
type BuyerData struct {
id int
balance float64
}
type AgentData struct {
id int
buyerData BuyerData
}
type GlobalData struct {
agents map[int]AgentData
}
if I wanted to define a "insert equivalent of Java's class method" for GlobalData to return a pointer to it's value buyerData on a given id, which would be a method with the following signature:
func (globalData *GlobalData) getBuyerData(id int) *BuyerData
What would be written inside it? I'm having troubles because it's giving all sorts of errors like cannot take the address of, invalid indirect of or does not support indexing...
This is what I currently have that does not generate any compiler exception:
func (globalData *GlobalData) getBuyerData(id int) *BuyerData {
var agents *map[int]AgentData = &(globalData.agents)
var agentData AgentData = (*agents)[id]
return &(agentData.buyerData)
}
But I, honestly, don't know what I'm doing or if this is the correct way of doing it.
Assuming you'd just like to return a pointer to the value in the map in a AgentData struct indexed by the argument of the function, you likely mean to say:
func (agentsGlobalData *AgentData) getBuyerData(id int) (*BuyerData, bool) {
buyer, has := agentsGlobalData.agents[id]
return &buyer, has
}
You might also consider storing pointers to AgentData in that map instead, making your struct:
type GlobalData struct {
agents map[int]*AgentData
}
But you haven't told us enough about your use case to recommend one or the other, perhaps.
(You say callers should be able to "write" to a BuyerData, but there are not any public fields in that struct...)
You need to say something like this:
func (globalData *GlobalData) GetBuyerData(id int) *BuyerData {
if agent, found := globalData.Agents[id]; found {
return &agent.BuyerData
}
return nil
}
See it here: https://play.golang.org/p/lwXn4D1mr3J
I am trying to define a custom type that returns a random number and use that type in a struct but I can't seem to get it right. Here is what I am doing
type genRandFunc func() int
func genRand() genRandFunc{
return func() int {
return rand.Intn(1000)
}
}
type User struct {
parseID genRand()
}
What am I doing wrong here?
Your User type definition is wrong. getRand() is a function call, not a type. Use
type User struct {
parseID genRandFunc
}
...
x:=User{parseID: getRand()}
There are some generated code I cannot change. They have the general structure like below:
// These structures & methods I cannot change
type NotMyStruct struct {
EmbeddedCaller
}
type EmbeddedCaller struct {
foobar string
}
func (_embeddedCaller *EmbeddedCaller) DoStuff() string {
return "ABC"
}
func NewNotMyStruct() *NotMyStruct {
return &NotMyStruct{
EmbeddedCaller{"blah"},
}
}
The general pattern of the generated code is 1) a parent struct + an embedded struct 2) a method on the embedded struct and 3) a New method that creates the struct.
I have a number of these generated "contracts" and they all have different types, ie NotMyStruct1 NotMyStruct2 etc etc. The embedded structs are all different types as well, ie EmbeddedCaller1, EmbeddedCaller2 etc.
However they all have the same method DoStuff with the same return value. What I would like to do is create a map of some id to the New functions then iterate over each of these and call the DoStuff method. However my code does not compile. it would look something like this if it compiled:
type MyDoStuffInterface interface {
DoStuff() string
}
var instantiations map[string]func()*MyDoStuffInterface{
"1": NewNotMyStruct, //<-- does not compile here because *MyDoStuffInterface doesn't match *NotMyStruct
...
}
for id, instantiationFunc := range instantiations {
instance := instantiationFunc()
instance.DoStuff()
}
Is it possible to do what I'm trying to do? If so how? If not, what is the easiest way to keep things dry?
First, you need to replace *MyDoStuffInterface with MyDoStuffInterface. Pointers to interfaces do have their uses, but nearly all of the time they aren't needed (or correct).
Second, the type of your function (func()*NotMyStruct) doesn't match func()MyDoStuffInterface. (People more experienced in types than me might say that function types in go aren't covariant or something like that).
The best way to solve this second problem is to use a wrapper function that has the correct type. (An alternative is to avoid the type system and use interface{} for your function type and use run-time reflection to call your function).
Here's a full compiling example (playground link). (I had to change your instantiations variable a little because the syntax for initializing a map wasn't correct.)
package main
type NotMyStruct struct {
EmbeddedCaller
}
type EmbeddedCaller struct {
foobar string
}
func (_embeddedCaller *EmbeddedCaller) DoStuff() string {
return "ABC"
}
func NewNotMyStruct() *NotMyStruct {
return &NotMyStruct{
EmbeddedCaller{"blah"},
}
}
type MyDoStuffInterface interface {
DoStuff() string
}
func main() {
var instantiations = map[string](func() MyDoStuffInterface){
"1": func() MyDoStuffInterface { return NewNotMyStruct() },
}
for _, instantiationFunc := range instantiations {
instance := instantiationFunc()
instance.DoStuff()
}
}
Use the following map:
var instantiations = map[string]func()MyDoStuffInterface{
"1": func() MyDoStuffInterface {
return NewNotMyStruct()
},
}
Some notes:
The anonymous "adaptor" function is required because NewNotMyStruct() returns a *NotMyStruct, not a MyDoStuffInterface.
Do not use a pointer to an interface. They are not needed.
Run it on the Go Playground.
Say I have a struct:
type DriverData struct {
TypePath string = "Foo.Bar.DriverData"
}
I want to be able to reference TypePath without having to create an instance of the struct, something like:
typePath := DriverData.TypePath
but that's not possible in Golang.
So I was wondering - maybe there is a way to create a map, and associate the type with a string, something like:
type DriverData struct {
}
type PilotData struct {
}
type BoatmasterData struct {
}
typeMap := map[struct]string{
DriverData: "Foo.Bar.DriverData",
PilotData: "Foo.Bar.PilotData",
BoatmasterData: "Foo.Bar.BoatmasterData",
}
Question:
Is this the best approach to create static properties on a struct? Storing the static properties in a map like this?
You can define methods to give you those values:
type DriverData struct {
}
func (DriverData) Path() string {
return "Foo.Bar.DriverData"
}
type PilotData struct {
}
func (PilotData) Path() string {
return "Foo.Bar.PilotData"
}
type BoatmasterData struct {
}
func (BoatmasterData) Path() string {
return "Foo.Bar.BoatmasterData"
}
Does that do what you want?
See https://play.golang.org/p/zR7RZwMVEdf.
How can to initialize any fields in golang types? For example:
type MyType struct {
Field string = "default"
}
You can't have "default" values like that, you can either create a default "constructor" function that will return the defaults or simply assume that an empty / zero value is the "default".
type MyType struct {
Field string
}
func New(fld string) *MyType {
return &MyType{Field: fld}
}
func Default() *MyType {
return &MyType{Field: "default"}
}
Also I highly recommend going through Effective Go.
There is no way to do that directly. The common pattern is to provide a New method that initializes your fields:
func NewMyType() *MyType {
myType := &MyType{}
myType.Field = "default"
return myType
// If no special logic is needed
// return &myType{"default"}
}
Alternatively, you can return a non-pointer type. Finally, if you can work it out you should make the zero values of your struct sensible defaults so that no special constructor is needed.