How to initialise nested structs in go? [duplicate] - struct

This question already has answers here:
How to initialize a nested struct?
(10 answers)
Closed 7 years ago.
Hi I am very new to Golang, please help me. I have defined a struct inside a struct. But I get an error when I try to initialise the main struct.
type DetailsFilter struct {
Filter struct {
Name string
ID int
}
}
var M map[string]interface{}
M = make(map[string]interface{})
M["Filter"] = map[string]interface{}{"Name": "XYZ", "ID": 5}
var detailsFilter = DetailsFilter{Filter: M["Filter"]}}
The error I get is : can not use (type interface {}) as type struct in field value : need type assertion.
Please suggest a way to initialise DetailsFilter.
I tried doing the method described in Initialize a nested struct in Golang, but even this is not working.

Unfortunately if the type of a struct field is an anonymous struct, at construction time you can only initialize it by "duplicating" the anonymous struct type (specifying it again):
type DetailsFilter struct {
Filter struct {
Name string
ID int
}
}
df := DetailsFilter{Filter: struct {
Name string
ID int
}{Name: "myname", ID: 123}}
fmt.Println(df)
Output:
{Filter:{Name:myname ID:123}}
Shorter Alternative
So instead I recommend not to initialize it at construction, but rather after the zero-valued struct has been created, like this:
df = DetailsFilter{}
df.Filter.Name = "myname2"
df.Filter.ID = 321
fmt.Printf("%+v\n", df)
Output:
{Filter:{Name:myname2 ID:321}}
Try it on the Go Playground.
Naming the anonymous struct type
Or don't use anonymous struct as field type at all, name the type like this:
type Filter struct {
Name string
ID int
}
type DetailsFilter struct {
Filter Filter
}
And then you can simply initialize it like this:
df := DetailsFilter{Filter: Filter{Name: "myname", ID: 123}}
fmt.Printf("%+v\n", df)
Output (try it on the Go Playground):
{Filter:{Name:myname ID:123}}

The type of M["Filter"] to the compiler is interface{}. The type to the runtime is map[string]interface{}. Neither of those is convertable to your anonymous struct type for the Filter field.
You should define a type for filter, or just not have it at all. Why not
type DetailsFilter struct {
Name string
ID int
}
M := make(map[string]*DetailsFilter)
M["Filter"] = &DetailsFilter{"Name": "XYZ", "ID": 5}
var detailsFilter = M["Filter"]
I am not sure why you need the nested anonymous struct at all. In general it is not needed to use interface{} unless you really need to. It is usually a sign you are fighting the type system instead of embracing it.
The reason You should not use an anonymous struct like that is that there is no clean way to intialize it. You have two options:
1- Repeat the type definition in the struct literal:
x := DetailsFilter{Filter: struct {
Name string
ID int
}{Name: "Foo", ID: 5}}
2- Set individual fields:
x := DetailsFilter{}
x.Filter.Name = "Foo"
x.Filter.ID = 5
It seems easier to create a real type for the nested field if you want to create them with struct literals.

Related

Is there a way to list methods of an object in GoLang? [duplicate]

The Golang "fmt" package has a dump method called Printf("%+v", anyStruct). I'm looking for any method to dump a struct and its methods too.
For example:
type Foo struct {
Prop string
}
func (f Foo)Bar() string {
return f.Prop
}
I want to check the existence of the Bar() method in an initialized instance of type Foo (not only properties).
Is there any good way to do this?
You can list the methods of a type using the reflect package. For example:
fooType := reflect.TypeOf(&Foo{})
for i := 0; i < fooType.NumMethod(); i++ {
method := fooType.Method(i)
fmt.Println(method.Name)
}
You can play around with this here: http://play.golang.org/p/wNuwVJM6vr
With that in mind, if you want to check whether a type implements a certain method set, you might find it easier to use interfaces and a type assertion. For instance:
func implementsBar(v interface{}) bool {
type Barer interface {
Bar() string
}
_, ok := v.(Barer)
return ok
}
...
fmt.Println("Foo implements the Bar method:", implementsBar(Foo{}))
Or if you just want what amounts to a compile time assertion that a particular type has the methods, you could simply include the following somewhere:
var _ Barer = Foo{}

Unexpected return of anonymous struct

I am trying to implement a method that returns a modified struct based on the original one, such as:
type Project struct {
Username string
Id uint
Alias string
Data *json.RawMessage
Scheme Scheme
}
func (p *Project) OmitUsername() *struct {
return &struct {
Id uint
Alias string
Data *json.RawMessage
Scheme Scheme
}{
p.Id,
p.Alias,
p.Data,
p.Scheme
})
}
And I get the following error:
models/project.go:22: syntax error: unexpected return
models/project.go:24: non-declaration statement outside function body
models/project.go:25: non-declaration statement outside function body
models/project.go:25: syntax error: unexpected string literal, expecting semicolon or newline
models/project.go:26: non-declaration statement outside function body
Any help would be appreciated.
With "truly" anonymous struct return value
If you want to use an anonymous struct return value, that's gonna look really ugly.
Why? Because when you define the return type, you have to describe the anonymous struct. And when you write a return statement, you have to provide the return value which will be a struct literal. A struct literal for an anonymous struct also has to describe the struct!
When you attempt to write this:
func (p *Project) OmitUsername() *struct {
// return somethig
}
This syntax is not what you think: it doesn't contain the struct definition. Basically in your example the first { is the opening bracket of the anonymous struct definition, and not the opening bracket of the function body. And as such, the subsequent return is interpreted as being inside the anonymous struct definition which is invalid syntax, this is exactly what the error message states too ("syntax error: unexpected return").
It should look like this:
func (p *Project) OmitUsername() *struct {
Id uint
Alias string
Data *json.RawMessage
Scheme Scheme
} {
// And now here comes the return statement
}
And if you also add the return statement which has to repeat the anonymous struct definition:
func (p *Project) OmitUsername() *struct {
Id uint
Alias string
Data *json.RawMessage
Scheme Scheme
} {
return &struct {
Id uint
Alias string
Data *json.RawMessage
Scheme Scheme
}{p.Id, p.Alias, p.Data, p.Scheme}
}
Yes, it's ugly. You can make it a little simpler by using named return value, and not returning a pointer, because zero value of pointers is nil, and to return something, you'd have to initialize it which would also involve repeating the anonymous struct! If you use a non-pointer, named return value, you will have a value of the anonymous struct right away, and you don't have to repeat the anonymous struct definition again, just assign values to its fields:
func (p *Project) OmitUsername2() (ret struct {
Id uint
Alias string
Data *json.RawMessage
Scheme Scheme
}) {
ret.Id = p.Id
ret.Alias = p.Alias
ret.Data = p.Data
ret.Scheme = p.Scheme
return
}
Using them:
p := Project{"Bob", 1, "bobie", nil, nil}
fmt.Println(p.OmitUsername())
fmt.Println(p.OmitUsername2())
Output (try these on the Go Playground):
&{1 bobie <nil> <nil>}
{1 bobie <nil> <nil>}
Still ugly...
With another named type, using embedding
...Best would be to provide another named type to return and not an anonymous struct. You may utilize embedding to make this solution practical and short:
type BaseProject struct {
Id uint
Alias string
Data *json.RawMessage
Scheme Scheme
}
type Project struct {
BaseProject
Username string
}
func (p *Project) OmitUsername() BaseProject {
return p.BaseProject
}
Using it:
p := Project{BaseProject{1, "bobie", nil, nil}, "Bob"}
fmt.Println(p.OmitUsername())
Output (try this on the Go Playground):
{1 bobie <nil> <nil>}
Note:
Embedding is not really necessary, but this way the fields of the embedded type (BaseProject) will be promoted and so you can refer to them like p.Id as if they were defined in Project. Defining it as a regular field would also work.
The Go Programming Language Specification
Keywords
The following keywords are reserved and may not be used as identifiers.
break default func interface select
case defer go map struct
chan else goto package switch
const fallthrough if range type
continue for import return var
.
func (p *Project) OmitUsername() *struct {
}
struct is a reserved keyword.
Without more information about what you are trying to do, it's hard to know what you want, pehaps something like this?
package main
import (
"encoding/json"
)
type Scheme struct{}
type Project struct {
Id uint
Alias string
Data *json.RawMessage
Scheme Scheme
}
type UserProject struct {
Username string
Project
}
func (u *UserProject) GetProject() *Project {
return &u.Project
}
func main() {}

Access String value in enum without using rawValue

I would like to replace my global string constants with a nested enum for the keys I'm using to access columns in a database.
The structure is as follows:
enum DatabaseKeys {
enum User: String {
case Table = "User"
case Username = "username"
...
}
...
}
Each table in the database is an inner enum, with the name of the table being the enum's title. The first case in each enum will be the name of the table, and the following cases are the columns in its table.
To use this, it's pretty simple:
myUser[DatabaseKeys.User.Username.rawValue] = "Johnny"
But I will be using these enums a lot. Having to append .rawValue to every instance will be a pain, and it's not as readable as I'd like it to be. How can I access the String value without having to use rawValue? It'd be great if I can do this:
myUser[DatabaseKeys.User.Username] = "Johnny"
Note that I'm using Swift 2. If there's an even better way to accomplish this I'd love to hear it!
While I didn't find a way to do this using the desired syntax with enums, this is possible using structs.
struct DatabaseKeys {
struct User {
static let identifier = "User"
static let Username = "username"
}
}
To use:
myUser[DatabaseKeys.User.Username] = "Johnny"
Apple uses structs like this for storyboard and row type identifiers in the WatchKit templates.
You can use CustomStringConvertible protocol for this.
From documentation,
String(instance) will work for an instance of any type, returning its
description if the instance happens to be CustomStringConvertible.
Using CustomStringConvertible as a generic constraint, or accessing a
conforming type's description directly, is therefore discouraged.
So, if you conform to this protocol and return your rawValue through the description method, you will be able to use String(Table.User) to get the value.
enum User: String, CustomStringConvertible {
case Table = "User"
case Username = "username"
var description: String {
return self.rawValue
}
}
var myUser = [String: String]()
myUser[String(DatabaseKeys.User.Username)] = "Johnny"
print(myUser) // ["username": "Johnny"]
You can use callAsFunction (New in Swift 5.2) on your enum that conforms to String.
enum KeychainKey: String {
case userId
case email
}
func callAsFunction() -> String {
return self.rawValue
}
usage:
KeychainKey.userId()
You can do this with custom class:
enum Names: String {
case something, thing
}
class CustomData {
subscript(key: Names) -> Any? {
get {
return self.customData[key.rawValue]
}
set(newValue) {
self.customData[key.rawValue] = newValue
}
}
private var customData = [String: Any]()
}
...
let cData = CustomData()
cData[Names.thing] = 56
Edit:
I found an another solution, that working with Swift 3:
enum CustomKey: String {
case one, two, three
}
extension Dictionary where Key: ExpressibleByStringLiteral {
subscript(key: CustomKey) -> Value? {
get {
return self[key.rawValue as! Key]
}
set {
self[key.rawValue as! Key] = newValue
}
}
}
var dict: [String: Any] = [:]
dict[CustomKey.one] = 1
dict["two"] = true
dict[.three] = 3
print(dict["one"]!)
print(dict[CustomKey.two]!)
print(dict[.three]!)
If you are able to use User as dictionary key instead of String (User is Hashable by default) it would be a solution.
If not you should use yours with a nested struct and static variables/constants.

Golang : Is conversion between different struct types possible?

Let's say I have two similar types set this way :
type type1 []struct {
Field1 string
Field2 int
}
type type2 []struct {
Field1 string
Field2 int
}
Is there a direct way to write values from a type1 to a type2, knowing that they have the same fields ?
(other than writing a loop that will copy all the fields from the source to the target)
Thanks.
To give a reference to OneOfOne's answer, see the Conversions section of the spec.
It states that
A non-constant value x can be converted to type T in any of these
cases:
x is assignable to T.
x's type and T have identical underlying types.
x's type and T are unnamed pointer types and their pointer base types have identical underlying types.
x's type and T are both integer or floating point types.
x's type and T are both complex types.
x is an integer or a slice of bytes or runes and T is a string type.
x is a string and T is a slice of bytes or runes.
The first and highlighted case is your case. Both types have the underlying type
[]struct { Field1 string Field2 int }
An underlying type is defined as
If T is one of the predeclared boolean, numeric, or string types, or a type literal, the corresponding underlying type is T itself. Otherwise, T's underlying type is the underlying type of the type to which T refers in its type declaration. (spec, Types)
You are using a type literal to define your type so this type literal is your underlying type.
For your specific example, you can easily convert it playground:
t1 := type1{{"A", 1}, {"B", 2}}
t2 := type2(t1)
fmt.Println(t2)
As of Go 1.8, struct tags are ignored when converting a value from one struct type to another. Types type1 and type2 will be convertible, regardless of their struct tags, in that Go release. https://beta.golang.org/doc/go1.8#language
Nicolas, in your later comment you said you were using field tags on the struct; these count as part of definition, so t1 and t2 as defined below are different and you cannot cast t2(t1):
type t1 struct {
Field1 string
}
type t2 struct {
Field1 string `json:"field_1"`
}
UPDATE: This is no longer true as of Go 1.8
This is not the standard way, but if you wish to have a flexible approach to convert a struct to, lets say, a map, or if you want to get rid of some properties of your struct without using `json:"-", you can use JSON marshal.
Concretely, here is what I do:
type originalStruct []struct {
Field1 string
Field2 int
}
targetStruct := make(map[string]interface{}) // `targetStruct` can be anything of your choice
temporaryVariable, _ := json.Marshal(originalStruct)
err = json.Unmarshal(temporaryVariable, &targetStruct)
if err != nil {
// Catch the exception to handle it as per your need
}
Might seem like a hack, but was pretty useful in most of my tasks.
for go v1.18 that already support generic, basically i just create method that accept any type of parameter and convert it to another type with json.Marshal / Unmarshal
// utils.TypeConverter
func TypeConverter[R any](data any) (*R, error) {
var result R
b, err := json.Marshal(&data)
if err != nil {
return nil, err
}
err = json.Unmarshal(b, &result)
if err != nil {
return nil, err
}
return &result, err
}
suppose that i have a struct called models.CreateUserRequest and i want to convert it to models.User. Note that json tag must be same
// models.CreateUserRequest
type CreateUserRequest struct {
Fullname string `json:"name,omitempty"`
RegisterEmail string `json:"email,omitempty"`
}
// models.User
type User struct {
Name string `json:"name,omitempty"`
Email string `json:"email,omitempty"`
Phone string `json:"phone,omitempty"`
}
I can use that utils method above like this
user := models.CreateUserRequest {
Name: "John Doe",
Email: "johndoe#gmail.com"
}
data, err := utils.TypeConverter[models.User](&user)
if err != nil {
log.Println(err.Error())
}
log.Println(reflrect.TypeOf(data)) // will output *models.User
log.Println(data)
You can manually use a mapper function which maps each element of type t1 to type t2. It will work.
func GetT2FromT1(ob1 *t1) *t2 {
ob2 := &t2 { Field1: t1.Field1, }
return ob2
}
Agniswar Bakshi's answer is faster and better if you can write those conversions manually, but here's an expansion on Furqan Rahamath's answer. (A more complete example is available on the Golang playground )
func Recast(a, b interface{}) error {
js, err := json.Marshal(a)
if err != nil {
return err
}
return json.Unmarshal(js, b)
}
// Usage:
type User struct {
Name string
PasswordHash string
}
// remove PasswordHash before providing user:
type PrivateOutgoingUser struct {
Name string
}
u1 := &User{Name: "Alice", PasswordHash: "argon2...."}
u2 := &PrivateOutgoingUser{}
err = Recast(u1, u2)
if err != nil {
log.Panic("Error recasting u1 to u2", err)
}
log.Println("Limited user:", u2)
Here's another way that uses JSON tagging which is faster, since it doesn't require that extra marshal-unmarshal step, but not quite as flexible:
type User struct {
Name string
PasswordHash string `json:"-"` // - removes the field with JSON
}
user := &User{Name: "Tommy Tester", PasswordHash: "argon2...."}
js, err := json.Marshal(user)
log.Println("Limited user:", string(user))

Two forms of struct declaration in Go

I've got two forms of struct declaration in the function scope. As far as I could see the bellow-listed snippet woks just fine. The question is what's the difference between the two declaration ways? Is that only a semantic question or there is something tricky under the covers?
package main
import "fmt"
func main() {
type Person1 struct {
Name string
Id int
}
person1 := &Person1{Name : "John Smith", Id : 10}
fmt.Printf("(%s, %d)\n", person1.Name, person1.Id)
var person2 struct {
name string
id int
}
person2.name = "Kenneth Box"
person2.id = 20
fmt.Printf("(%s, %d)\n", person2.name, person2.id)
}
One is a named type - you can create multiple variables of that type, if you need to, using the type name.
The other type has no name. You cannot create more variables of the type other than by using the := operator.
person1 is a pointer to a struct, while person2 is a struct value itself. If you had done person1 := Person1{Name : "John Smith", Id : 10} then it would be the same

Resources