I'm coming from Java and trying to figure out how Go's type system works. I want to create a simple graph data structure and implement breadth first search. This is what I have so far.
package graph
import "fmt"
type Node struct {
neighbors []Edge
visited bool
name string
}
type Edge struct {
neighbor Node
}
type Graph struct {
nodes []Node
}
func (g *Graph) addNode(node Node) {
g.nodes = append(g.nodes, node)
}
func (n *Node) addEdge(neighbor Node) {
edge := Edge{
neighbor: neighbor,
}
n.neighbors = append(n.neighbors, edge)
}
func (g Graph) String() {
for _, node := range g.nodes {
//fmt.Printf("nodename: %v", node.name)
fmt.Println(len(node.neighbors))
if len(node.neighbors) > 0 {
fmt.Print("node: %v, edges: ", node.name)
for _, e := range node.neighbors {
fmt.Print(e.neighbor.name)
}
}
}
}
when I try to run it with test code:
func TestGraph(t *testing.T) {
graph := Graph{}
n1 := Node { name: "abc", }
n2 := Node { name: "def", }
graph.addNode(n1)
graph.addNode(n2)
n1.addEdge(n2)
graph.String()
}
in my String() method, the len(node.neighbors) is always 0. What am I doing wrong? I thought that since I take in a reference type in addEdge, it modifies the node reference but I'm obviously I'm missing something about Go's type system.
This isn't a type system issue, but an issue with how data is passed in Go.
I think the root misunderstanding is about "passing by reference". In Go, everything is passed by value, there is no pass by reference (https://golang.org/doc/faq#pass_by_value)
So when you pass a Node
struct in to the addEdge
method, it's actually making a copy of that struct.
If you want to refer to the same underlying struct instead of copying it you should pass the pointer to it.
Try the following slightly modified code, that uses pointers to pass the structs: (You can tweak and run the code here: https://play.golang.org/p/Qsbi4LBXS4)
package main
import "fmt"
type Node struct {
neighbors []*Edge
visited bool
name string
}
type Edge struct {
neighbor *Node
}
type Graph struct {
nodes []*Node
}
func (g *Graph) addNode(node *Node) {
g.nodes = append(g.nodes, node)
}
func (n *Node) addEdge(neighbor *Node) {
edge := &Edge{
neighbor: neighbor,
}
n.neighbors = append(n.neighbors, edge)
}
func (g Graph) String() {
for _, node := range g.nodes {
//fmt.Printf("nodename: %v", node.name)
fmt.Printf("number of neighbors: %d
", len(node.neighbors))
if len(node.neighbors) > 0 {
fmt.Printf("node: %v, edges: ", node.name)
for _, e := range node.neighbors {
fmt.Printf("%q", e.neighbor.name)
}
fmt.Println()
}
}
}
func main() {
graph := &Graph{}
n1 := &Node{name: "abc"}
n2 := &Node{name: "def"}
graph.addNode(n1)
graph.addNode(n2)
n1.addEdge(n2)
graph.String()
}
You need to pass your variables by pointer or a new value is created
func (g *Graph) addNode(node *Node) {
g.nodes = append(g.nodes, node)
}
...
n1 := Node { name: "abc", }
graph.addNode(&n1)
...
n1.addEdge(&n2)