-1

(The previously linked "answer" does not answer this question. stackoverflow.com/questions/24809235/initialize-a-nested-struct. Please do not close this question unless you can provide a clear answer.)

In this nested struct example testJSON, I'm getting an error Foo is undefined.

https://play.golang.com/p/JzGoIfYPNjZ

Not sure what the correct way to assign values using TestStruct are in the case of the Foo property.

// TestStruct a test struct
type TestStruct struct {
    Foo struct {
        Thing string `json:Thing`
    } `json:Foo`
}

var testJSON = TestStruct{
    Foo: Foo{
        Thing: "test thing string",
    },
}
Geuis
  • 41,122
  • 56
  • 157
  • 219
  • 2
    There is no `type Foo struct { ...` declaration in your code, hence undefined. What you have there is field Foo whose type is an **anonymous** struct. Therefore, trying to do `Foo{}` to initialize an anonymous struct results in a compiler error. – mkopriva Nov 14 '19 at 21:15
  • 1
    So use declared types instead of anonymous ones. If you have to use anonymous structs you have to initialize them as such, i.e. `Foo: struct { Thing string }{Thing: "this thing ..." }` but as you can see this is ugly and verbose, so, again, use named types. Last resort, use anonymous as you do, but set the fields value "outside" of the *literal*, i.e. `var testJSON = TestStruct{}; testJSON.Foo.Thing = "test thing"`. – mkopriva Nov 14 '19 at 21:19
  • Thanks @mkopriva. But my example is declared in the same fashion as described on https://medium.com/@xcoulon/nested-structs-in-golang-2c750403a007. What's the difference? – Geuis Nov 14 '19 at 21:19
  • 1
    You're not doing this from the article: https://gist.github.com/xcoulon/7d0d61743b97b21d8c157dd536c13530#file-nested_structs_init-go. The difference is you're doing `Foo: Foo{ ... }` and they are doing `Foo: struct { Thing string }{ ... }` – mkopriva Nov 14 '19 at 21:22
  • 1
    See a working version of your attempt: https://play.golang.com/p/h3b0kotV4uz compare it your's and you should see the difference, in case it's not clear yet. – mkopriva Nov 14 '19 at 21:24
  • @mkopriva Thanks for the tips. Did some more reading and I think I understand now. You can't have nested type declarations in golang. I think that's an odd language choice, but it is what it is. – Geuis Nov 14 '19 at 22:22
  • @Geuis - you can nest them but yeah you have to declare a separate `type Foo`. https://play.golang.com/p/zypQtqLDg_2 – rgin Nov 15 '19 at 06:20
  • "You can't have nested type declarations in golang" is not true. You absolutely can. The syntax just gets more verbose, so it generally isn't done when you need to instantiate instances of the type; it would only be used where the type is only used through reflection (e.g. when unmarshalling JSON). – Adrian Nov 15 '19 at 14:20

2 Answers2

0

Try by making Foo it's own struct.

package main

import (
    "fmt"
)

// TestStruct a test struct
type TestStruct struct {
    // you have to make the Foo struct by itself
    Foo
}

type Foo struct {
    Thing string
}

var testJSON = TestStruct{
    Foo: Foo{
        Thing: "test thing string",
    },
}

func main() {
    fmt.Println("Hello, playground")
}

If you want to read about nested Structs, this might help.

WEBjuju
  • 5,797
  • 4
  • 27
  • 36
0

The error is accurate: Foo is undefined. There is no type Foo that your use of Foo in a literal here can be referring to. You have a field Foo, but its type is the anonymous type struct { Thing string }. So, to fill that field with a literal, you would have to use its correct type name, which is not Foo, it is struct { Thing string }:

var testJSON = TestStruct{
    Foo: struct {
        Thing string
    }{
        Thing: "test thing string",
    },
}

Most developers don't like to be this verbose with types they actually need to refer to, so they'll use named types for this case:

type TestStruct struct {
    Foo Foo `json:Foo`
}

type Foo struct {
    Thing string `json:Thing`
}

In which case your existing creation code would work fine. Anonymous types are most commonly used in cases where they don't need to be referred to, i.e. where reflection is the only way the types will be instantiated. This is the case when you want to unmarshal some JSON into a type, but you never want to create an instance of the type programatically. You'll even see cases where the outermost type isn't named, like:

type TestStruct struct {
    Foo struct {
        Thing string `json:Thing`
    } `json:Foo`
}

var testJSON = struct {
    Foo struct {
        Thing string `json:Thing`
    } `json:Foo`
}{}

json.Unmarshal(something, &testJSON)

fmt.Printf(testJSON.Foo.Thing)

This can be useful when you want to unmarshal a complex JSON document just to grab some deeply nested field(s).

Adrian
  • 42,911
  • 6
  • 107
  • 99