Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Golang copying structures that contain pointers

TL;DR Because of the downvotes and the lack of answers and the comments I assume a tl;dr is needed.

How can I create a struct in golang, which contains a pointer, then safely pass it by value to other functions ? (By safely I mean without having to worry that those functions can dereference said pointer and change the variable its pointing to).

AND

If the answer you are going to give is "Copy functions" then how can I delete the original copy constructor/operator ? Override it with me custom copy function ? Or otherwise discourage people from using it ?


In golang I can have a structure that contains a pointer to a dynamically allocated variable.

I can also pass instances of those structures to functions that "copy" them.

However, I cannot override or delete the built-in copy operator. This means that, in theory, I could have code like the following one:

import (
        "fmt"
)

type A struct {
        a * int
}

func main() {
        var instance A
        value := 14
        instance.a = &value
        fmt.Println(*instance.a) // prints 14
        mutator(instance)
        fmt.Println(*instance.a) // prints 11 o.o
}

func mutator(instance A) {
        *instance.a = 11
        fmt.Println(*instance.a)
}

This type of code is obviously a bit nonsensical here. However, assuming the member field "a" was a complex structure it might stand within reason that a function accessing it will try and modify it.

It might also stand within reason that once the function "mutator" is called the programmer might want to keep on using his instance of A and (assuming he didn't necessarily code the structure or was aware of its internals) might even assume that since he passed a copy rather than a pointer his instance of A will remain unchanged.

Now, there are several (3) popular languages which allow the programmer to think about allocating and manipulating memory that aren't golang. I don't know Rust or C so I will refrain to how I would tackle this problem in C++:

a) Assuming I was the designer of the class A I could build a copy constructor resulting in the following code:

#include <iostream>

    class A {
    public:
            int * a;
            A(int value): a(new int{value}) {}
            A(const A & copyFrom): a(new int{*copyFrom.a}) {}
    };

    void mutator(A instance) {
            *instance.a = 11;
            std::cout << *instance.a << "\n";
    }


    int main() {
            A instance{14};
            std::cout << *(instance.a) << "\n";
            mutator(instance);
            std::cout << *instance.a << "\n";
    }

This allows an instance of my class to be copied with the added caveat that the pointer is also reassigned.

b) Assuming I was the designer of class A and didn't want to build the copy constructor (say that whatever a points to might be very large or that A is often used in performance critical conditions as a read only object) yet wanted to make sure that any assignment to copy couldn't modify the value a is pointing to (but still allow people to modify a by assigning it to a new value) I could write my class like this:

class A {
public:
        const int * a;
        A(int value): a(new const int{value}) {}
};

Which would make the following code to fail to compile:

void mutator(A instance) {
        *instance.a = 11;
        std::cout << *instance.a << "\n";
}


int main() {
        A instance{14};
        std::cout << *(instance.a) << "\n";
        mutator(instance);
        std::cout << *instance.a << "\n";
}

But the following code would compile just fine:

void mutator(A instance) {
        instance.a = new const int{11};
        std::cout << *instance.a << "\n";
}


int main() {
        A instance{14};
        std::cout << *(instance.a) << "\n";
        mutator(instance);
        std::cout << *instance.a << "\n";
}

Now, mind you, this is typical of C++'s "Object Oriented" (eegh) design. I for one would much prefer if I could have some sort of rule in the function signature that guaranteed no modification of the instance of A passed to it or a method by which to declare the instance of A "const" and "guard" its dynamically allocated fields (not only the static ones) against re-assignment.

However, whilst the solution may not be perfect, it is a solution. It allows me to have a clear idea about the "ownership" of my instances of A.

In golang, it appears that any "copy" of an instance that contains pointers is basically free for all, it can't be safely passed around even if the author of the struct had such an intention.

The one thing I can think of is having a "Copy" method that return a brand new instance of the structure (similar to the Copy Constructor in the example above). But without the ability to delete the copy constructor/operator it would be hard to make sure people will use and/or notice it.

To be honest it seems quite strange to me that golang even allows re-writing the memory address of a pointer without using the "unsafe" package or something similar.

Wouldn't it make more sense to simply prohibit this type of operation much like many others are ?

Considering the way "append" works it seems quite obvious that the intent of the authors is to favor re-assigning new variables to a pointer rather than mutating the one it previously pointed to. However, whilst this is easy to enforce with a built in structure like a slice or an array it seems quite hard to enforce with a custom struct (without, at least, wrapping said struct in a package).

Am I overlooking a way to do copy construction (or prohibit copying) in golang ? Is it indeed the original intent of the authors to encourage re assignment rather than mutation when memory and time permit it ? If so why is it so easy to mutate dynamically allocated variables ? Is there a way to mimic private/public behavior with structs or files rather than full fledged packages ? Are there any other way to enforce some semblance of ownership with structs that have pointers which I am overlooking ?

like image 550
George Avatar asked Oct 30 '22 08:10

George


1 Answers

How can I create a struct in golang, which contains a pointer, then safely pass it by value to other functions ? (By safely I mean without having to worry that those functions can dereference said pointer and change the variable its pointing to).

Use an exported package type with unexported fields. For example,

src/ptrstruct/ptrstruct.go:

package ptrstruct

type PtrStruct struct {
    pn *int
}

func New(n int) *PtrStruct {
    return &PtrStruct{pn: &n}
}

func (s *PtrStruct) N() int {
    return *s.pn
}

func (s *PtrStruct) SetN(n int) {
    *s.pn = n
}

func (s *PtrStruct) Clone() *PtrStruct {
    // make a deep clone
    t := &PtrStruct{pn: new(int)}
    *t.pn = *s.pn
    return t
}

src/ptrstruct.go:

package main

import (
    "fmt"

    "ptrstruct"
)

func main() {
    ps := ptrstruct.New(42)
    fmt.Println(ps.N())
    pc := ps.Clone()
    fmt.Println(pc.N())
    pc.SetN(7)
    fmt.Println(pc.N())
    fmt.Println(ps.N())
}

Output:

src $ go run ptrstruct.go
42
42
7
42
src $ 

If the answer you are going to give is "Copy functions" then how can I delete the original copy constructor/operator ? Override it with me custom copy function ? Or otherwise discourage people from using it ?

Stop programming in C++; start programming in Go. By design, Go is not C++.

"copy constructor/operator" and "Override it with custom function" are C++ concepts.

References:

The Go Programming Language Specification

Blocks

Declarations and scope

Exported identifiers

like image 59
peterSO Avatar answered Dec 04 '22 16:12

peterSO