G4143
G4143

Reputation: 2829

Structures with Interface Members and Pointer Receivers

I'm creating a simple sorted binary tree that's immutable (well it should behave like its immutable) and I'm unsure how pointer receivers work when they are passed a structure with interfaces.

Here's how I'm defining my binary tree.

type btree interface {
    display(io.Writer)
    add(int) btree
    replace(int, int)//A test to see if we are sharing nodes
}

binary tree nodes are defined like:

type node struct {
    data  int
    left  btree
    right btree
}

and the empty binary tree node

type empty struct{}

The function and methods

func createEmpty() btree {
    return &empty{}
}

Methods for the node struct

//replace is just a test to see if I'm sharing nodes
func (n *node) replace(value, replacement int) {
    if n.data < value {
        n.left.replace(value, replacement)
    } else if n.data > value {
        n.right.replace(value, replacement)
    } else {
        n.data = replacement
    }
}

func (n *node) add(data int) btree {
    if n.data < data {
        l := &node{n.data, n.left.add(data), n.right}
        return l
    } else if n.data > data {
        r := &node{n.data, n.left, n.right.add(data)}
        return r
    } else {
        return n
    }
}

func (n *node) display(w io.Writer) {
    n.left.display(w)
    fmt.Fprintln(w, n.data)
    n.right.display(w)
}

Methods for the empty nodes

//replace is just a test to see if I'm sharing nodes
func (*empty) replace(_, _ int) {}

func (e *empty) add(data int) btree {
    en := &node{data, e, e}
    return en
}

func (*empty) display(w io.Writer) {
    fmt.Fprintln(w, "Empty")
}

Please note that the code does work as intended but I'm unsure what happens when I pass a structure with interface members to a pointer receiver. Does the interface data-structure get copied but only a shallow copy so the data it points to remains the same? Is there any documentation on what happens to interfaces in this case?

Here's a working copy of the code below:

package main

import (
    "fmt"
    "io"
    "os"
)

func createEmpty() btree {
    return &empty{}
}

type btree interface {
    display(io.Writer)
    add(int) btree
    replace(int, int)
}

type node struct {
    data  int
    left  btree
    right btree
}

func (n *node) replace(value, replacement int) {
    if n.data < value {
        n.left.replace(value, replacement)
    } else if n.data > value {
        n.right.replace(value, replacement)
    } else {
        n.data = replacement
    }
}

func (n *node) add(data int) btree {
    if n.data < data {
        l := &node{n.data, n.left.add(data), n.right}
        return l
    } else if n.data > data {
        r := &node{n.data, n.left, n.right.add(data)}
        return r
    } else {
        return n
    }
}

func (n *node) display(w io.Writer) {
    n.left.display(w)
    fmt.Fprintln(w, n.data)
    n.right.display(w)
}

type empty struct{}

func (*empty) replace(_, _ int) {}

func (e *empty) add(data int) btree {
    en := &node{data, e, e}
    return en
}

func (*empty) display(w io.Writer) {
    fmt.Fprintln(w, "Empty")
}

func main() {
    bt := createEmpty().add(5).add(1).add(8).add(2)
    bt2 := bt.add(7).add(6).add(10).add(9)
    bt.display(os.Stdout)
    fmt.Fprintln(os.Stdout, "\n--->")
    bt2.display(os.Stdout)
    fmt.Fprintln(os.Stdout, "\n\n--->")
    //The node with 1 should be shared with bt and bt2
    //so if we change bt's node 1 to 4143
    //we should see the change in bt2 too   
    bt.replace(1, 4143)
    bt.display(os.Stdout)//We see the change here, 1 is now 4143
    fmt.Fprintln(os.Stdout, "\n--->")
    bt2.display(os.Stdout)//We see the change here, 1 is now 4143
}

Upvotes: 0

Views: 68

Answers (1)

Burak Serdar
Burak Serdar

Reputation: 51512

Given this structure:

type node struct {
    data  int
    left  btree
    right btree
}

and the function f with q pointer receiver:

func (n *node) f() {}

f gets a pointer to a node. Any changes f makes to the node n will be reflected on the copy that is used to invoke f.

To be more precise, when you call n.f() and if n is a *node, then a copy of that pointer is sent to f, which is still pointing to the same object as n. If n is not a pointer, then &n will be sent to f.

For your code to work, the left and right btree interfaces should also containg *nodes, not nodes (you already did this correctly). This is because if you call, for instance, node.left.replace, you want the value in the node.left to be replaced. If you had a value-receiver for replace function, then when you call node.left.replace a copy of that left node will be sent to the replace function as a receiver, and the modifications won't be visible on the node.left node.

Hope this helps.

Upvotes: 1

Related Questions