0

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.

1
  • 1
    FWIW String() is usually a method String() string that fulfils the fmt.Stringer (golang.org/pkg/fmt/#Stringer) interface. Most Go programmers will be surprised seeing a String() that doesn't return string Commented Oct 3, 2017 at 3:39

2 Answers 2

4

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\n", 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()
}
Sign up to request clarification or add additional context in comments.

Comments

1

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)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.