英文:
Golang copying structures that contain pointers
问题
简而言之,问题是如何在Go语言中创建一个包含指针的结构体,并安全地将其按值传递给其他函数(安全指的是不必担心这些函数会解引用该指针并更改其指向的变量)。另外,如果答案是"复制函数",那么如何删除原始的复制构造函数/操作符?可以用自定义的复制函数覆盖它吗?或者以其他方式阻止人们使用它?
在Go语言中,无法重写或删除内置的复制操作符。这意味着理论上可以有以下代码:
import (
"fmt"
)
type A struct {
a *int
}
func main() {
var instance A
value := 14
instance.a = &value
fmt.Println(*instance.a) // 输出 14
mutator(instance)
fmt.Println(*instance.a) // 输出 11
}
func mutator(instance A) {
*instance.a = 11
fmt.Println(*instance.a)
}
这种代码在这里显然有点荒谬。然而,假设成员字段"a"是一个复杂的结构体,函数访问它时可能会尝试修改它是合理的。
而且,一旦调用了函数"mutator",程序员可能希望继续使用他的A实例,并且(假设他没有编写结构体或者不了解其内部情况)甚至可能认为,由于他传递的是副本而不是指针,他的A实例将保持不变。
在其他几种流行的语言(如C++)中,可以通过复制构造函数来解决这个问题。但在Go语言中,目前没有直接的方法来实现类似的功能。
因此,目前最常见的解决方案是使用自定义的复制方法,返回一个全新的结构体实例。但由于无法删除复制构造函数/操作符,很难确保人们会使用或注意到这个方法。
总的来说,Go语言似乎允许对指针进行重新赋值,而无需使用"unsafe"包或类似的方法。这种操作是否合理呢?考虑到"append"函数的工作方式,似乎作者的意图是鼓励重新分配新的变量给指针,而不是修改它之前指向的变量。然而,对于自定义的结构体来说,要实现这一点似乎很困难(至少不包装在一个包中)。
你是否忽略了在Go语言中进行复制构造(或禁止复制)的方法?作者的初衷是否确实是在内存和时间允许的情况下鼓励重新分配而不是修改?如果是这样,为什么修改动态分配的变量如此容易?是否有一种方法可以模拟结构体或文件的私有/公共行为,而不是完整的包?是否有其他方法可以强制执行结构体的所有权概念,特别是对于包含指针的结构体?
英文:
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 ?
答案1
得分: 2
如何在golang中创建一个包含指针的结构体,并安全地将其按值传递给其他函数?(安全地意味着不必担心这些函数会解引用该指针并更改其指向的变量)。
使用具有未导出字段的导出包类型。例如,
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())
}
输出:
src $ go run ptrstruct.go
42
42
7
42
src $
如果你要给出的答案是"复制函数",那么如何删除原始的复制构造函数/操作符?用自定义的复制函数覆盖它?或者以其他方式阻止人们使用它?
停止使用C++编程,开始使用Go编程。按设计,Go不是C++。
"复制构造函数/操作符"和"用自定义函数覆盖它"是C++的概念。
参考资料:
英文:
> 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:
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论