I am trying to find out in a := b
if a
will be a different copy of struct like func pass_by_value(a some_struct)
But I find that I don't understand the print statement.
Consider this go playground
nested_level2 := test_assign_nested_level2{}
nested_level1 := test_assign_nested{nested_level2}
top_level := test_assign{nested_level1}
assign := top_level
fmt.Println("top_level address")
fmt.Printf("%p", &top_level)
fmt.Println(" ")
fmt.Println("1 level address")
fmt.Printf("%p", &top_level.Level1)
fmt.Println(" ")
fmt.Println("2 level address")
fmt.Printf("%p", &top_level.Level1.Level_2)
fmt.Println("------------------------")
fmt.Println("assign top_level address")
fmt.Printf("%p", &assign)
fmt.Println(" ")
fmt.Println("1 level address")
fmt.Printf("%p", &assign.Level1)
fmt.Println(" ")
fmt.Println("2 level address")
fmt.Printf("%p", &assign.Level1.Level_2)
The output of the above is
top_level address
0x10410020
1 level address
0x10410020
2 level address
0x10410020
assign top_level address
0x10410024
1 level address
0x10410024
2 level address
0x10410024
I expect the output to be similar to
fmt.Println("top_level address")
fmt.Printf("%p", &top_level)
fmt.Println(" ")
fmt.Println("1 level address")
fmt.Printf("%p", &nested_level1)
fmt.Println(" ")
fmt.Println("2 level address")
fmt.Printf("%p", &nested_level2)
fmt.Println(" ")
fmt.Println(" ------------------------------- ")
where
top_level address
0x421152280
1 level address
0x421152270
2 level address
0x421152260
each struct have a different address. but it seems like the child struct have the same address as the parent struct.
Why does all the nested element in the struct have the same address?
And does :=
actually copy a new struct
recursively? Like the print statement indicated? (ie. :=
will return a brand new copy of struct with each of its field content also a brand new copy recursively)
My guess is that you are looking for something like this:
package main
import "fmt"
// Nesting: type T2 struct{ F3 int }
type T2 struct{ F3 int }
// Nesting: type T1 struct{ { F2 struct{ F3 int } }
type T1 struct{ F2 T2 }
// Nesting: type T0 struct{ F1 struct{ F2 struct{ F3 int } } }
type T0 struct{ F1 T1 }
func main() {
t2 := T2{F3: 42}
fmt.Printf(
"%p %p %d
",
// 0xc4200120d0 0xc4200120d0 42
&t2, &t2.F3, t2.F3,
)
t1 := T1{F2: t2}
fmt.Printf(
"%p %p %p %d
",
// 0xc4200120f0 0xc4200120f0 0xc4200120f0 42
&t1, &t1.F2, &t1.F2.F3, t1.F2.F3,
)
t0 := T0{F1: t1}
fmt.Printf(
"%p %p %p %p %d
",
// 0xc420012100 0xc420012100 0xc420012100 0xc420012100 42
&t0, &t0.F1, &t0.F1.F2, &t0.F1.F2.F3, t0.F1.F2.F3,
)
}
Output:
0xc4200120d0 0xc4200120d0 42
0xc4200120f0 0xc4200120f0 0xc4200120f0 42
0xc420012100 0xc420012100 0xc420012100 0xc420012100 42
For T0
type T0 struct{ F1 T1 }
type T1 struct{ F2 T2 }
type T2 struct{ F3 int }
is equivalent to
type T0 struct {
F1 struct {
F2 struct {
F3 int
}
}
}
and T0
, F1
, F2
, and F3
have the same address.
In your new example, T0
type T2 struct{ F3A, F3B int }
type T1 struct{ F2 T2 }
type T0 struct{ F1 T1 }
is equivalent to
type T0 struct {
F1 struct {
F2 struct {
F3A int
F3B int
}
}
}
and T0
, F1
, F2
, and F3A
have the same address. F3A
and F3B
have different addresses.
Types in Go are not autonomes wrappers around other things but just two things: Types allow to attach methods and types provide a layout in memory. For the question here the ability to attach methods to types is irrelevant. Let's take a look a neutral formulation of the question:
type A int64
type B { a A }
type C { b B }
This declares three types with the following memory layout:
Type B in regard to memory layout is not a "wrapper" around A, at least the wrapper adds absolutely nothing. From a purely memory-layout perspective defining type B would be useless (but it allows to attach different methods to B than to A).
Now it should be clear that the address of a c C
is that of the first of its 8 bytes and this is the same as c.b's address which is the same than c.b.a. And any assignment to a C is just a copy of one machine word (on 64bit architectures).
If you define type D { a A; b B }
it gets more interesting as a D is now 16 bytes long. (Adding more to a D might even leave holes due to padding.) But still does D not provide anything (expect a new method set) to an A and a B adjacent to each other in memory.