douzhanglun4482 2017-04-25 23:07
浏览 41
已采纳

包含指针的Golang复制结构

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 << "
";
    }


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

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 << "
";
}


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

But the following code would compile just fine:

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


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

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 ?

  • 写回答

1条回答 默认 最新

  • douba8758 2017-04-26 12:17
    关注

    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

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥200 总是报错,能帮助用python实现程序实现高斯正反算吗?有偿
  • ¥15 对于squad数据集的基于bert模型的微调
  • ¥15 为什么我运行这个网络会出现以下报错?CRNN神经网络
  • ¥20 steam下载游戏占用内存
  • ¥15 CST保存项目时失败
  • ¥15 树莓派5怎么用camera module 3啊
  • ¥20 java在应用程序里获取不到扬声器设备
  • ¥15 echarts动画效果的问题,请帮我添加一个动画。不要机器人回答。
  • ¥15 Attention is all you need 的代码运行
  • ¥15 一个服务器已经有一个系统了如果用usb再装一个系统,原来的系统会被覆盖掉吗