英文:
What is the rationale of Go not having the const qualifier?
问题
我是一名C++高级程序员。我目前正在进行一些Go编程。我真正想要的唯一特性是const限定符。在Go中,如果你想修改一个对象,你需要传递它的指针。如果你不想修改它,你可以通过值传递。但是如果结构体很大,你应该通过指针传递,这就覆盖了不修改的特性。更糟糕的是,你可以通过值传递一个对象,但如果它包含一个指针,你实际上可以修改它的内容,这会带来可怕的竞态条件危险。一些语言类型,如映射和切片,具有这个特性。这在一个被认为是为并发而构建的语言中发生。因此,在Go中避免修改的问题实际上是不存在的,如果不打算修改,你应该通过值传递小对象(你必须意识到对象不包含指针)。
有了const,你可以通过const指针传递对象,而不用担心修改。类型安全是关于拥有一个允许速度和防止与类型相关的错误的契约。另一个具有这种功能的特性是const限定符。
英文:
I'm a C++ senior programmer. I'm currently doing some Go programming. The only feature I really miss is the const qualifier. In go, if you want to modify an object, you pass its pointer. If you don't want to modify it, you pass it by value. But if the struct is big, you should pass it by pointer, which overrides the no-modification feature. Worse, you can pass an object by value, but if it contains a pointer, you can actually modify its contents, with terrible race condition dangers. Some language types like maps and slices have this feature. This happens in a language that's supposed to be built for concurrency. So the issue of avoiding modification is really non-existent in Go, and you should pass small objects that do not contain pointers (you must be aware that the object does not contain a pointer) by value, if they aren't gonna be modified.
With const, you can pass objects by const pointer and don't worrying about modification. Type-safety is about having a contract that allows speed and prevents type-related bugs. Another feature that does this too is the const qualifier.
答案1
得分: 6
在C/C++中,const类型限定符有不同的含义。当应用于变量时,它意味着该变量是不可变的。这是一个有用的特性,在Go语言中缺少,但这似乎不是你所讨论的那个。
你所说的是const可以作为函数的部分强制约定的方式。函数可以给指针参数添加const限定符,表示该函数不会使用该指针改变任何值。(当然,除非函数使用了一个转换(在C++中是const_cast
)。或者,在C++中,指针指向一个声明为mutable
的字段。)
Go语言有一个非常简单的类型系统。许多语言都有一个复杂的类型系统,通过编写类型来强制执行程序的正确性。在许多情况下,这意味着大量的编程工作涉及编写类型声明。Go采取了不同的方法:大部分编程工作涉及编写代码,而不是类型。你通过编写正确的代码来编写正确的代码,而不是通过编写捕捉你编写错误代码的类型。如果你想捕捉错误的代码,你可以编写像go vet
这样的分析器,它会查找你的代码中无效的情况。这种类型的分析器在Go语言中比在C/C++中要容易得多,因为Go语言更简单。
这种方法有优点和缺点。Go在这里做出了明确的选择:编写代码,而不是类型。这对每个人来说都不是正确的选择。
英文:
The const type qualifier in C/C++ has various meanings. When applied to a variable, it means that the variable is immutable. That's a useful feature, and one that is missing from Go, but it's not the one you seem to be talking about.
You are talking about the way that const can be used as a partially enforced contract for a function. A function can give a pointer parameter the const qualifier to mean that the function won't change any values using that pointer. (Unless, of course, the function uses a cast (a const_cast
in C++). Or, in C++, the pointer points to a field that is declared mutable
.)
Go has a very simple type system. Many languages have a complex type system in which you enforce the correctness of your program by writing types. In many cases this means that a good deal of programming involves writing type declarations. Go takes a different approach: most of your programming involves writing code, not types. You write correct code by writing correct code, not by writing types that catch cases where you write incorrect code. If you want to catch incorrect code, you write analyzers, like go vet
that look for cases that are invalid in your code. These kinds of analyzers are much much easier to write for Go than for C/C++, because the language is simpler.
There are advantages and disadvantages to this kind of approach. Go is making a clear choice here: write code, not types. It's not the right choice for everyone.
答案2
得分: 2
请将其视为一条扩展评论。我不是任何编程语言的设计者,所以无法深入探讨细节,但我会以长期使用C++和短期使用Go的开发者的身份表达我的观点。
对于编译器来说,const是一个非平凡的特性,所以我们必须确保它为用户提供足够的优势,并且不会牺牲语法的简洁性。你可能认为我们只是在谈论const
限定符,但看看C++本身,情况并不那么简单,有很多注意事项。
-
你说
const
是一个合约,不应该在任何情况下都能修改它。你反对使用只读接口的一个论点是你可以将其转换为原始类型并做任何你想做的事情。当然你可以。同样的方式,你可以使用const_cast
在C++中违反合约。由于某种原因,它被添加到语言中,虽然我不确定是否应该为此感到自豪,但我曾经使用过一两次。 -
在C++中还有另一个修饰符允许你放宽合约——
mutable
。有人意识到const
结构可能需要修改一些字段,通常是保护内部变量的互斥锁。我猜在Go中你可能需要类似的东西,以便能够实现线程安全的结构。 -
当涉及到简单的
const int x
时,人们可以很容易地理解。但是当指针出现时,人们真的会感到困惑。const int * x
,int * const x
,const int * const x
- 这些都是x
的有效声明,每个声明都有不同的合约。我知道选择正确的声明并不是一件难事,但作为一名资深的C++程序员,你的经验告诉你人们是否广泛理解这些并且总是使用正确的声明?我甚至还没有提到像const int * const * * * const * const x
这样的东西。这让我感到困惑。
在我转到第4点之前,我想引用以下内容:
> 更糟糕的是,你可以按值传递一个对象,但如果它包含一个指针,你实际上可以修改它的内容。
现在这是一个有趣的指责。在C++中也存在同样的问题;更糟糕的是,即使你将对象声明为const,它仍然存在,这意味着你不能通过简单的const
限定符来解决这个问题。看看下一个点:
-
根据第3点和指针,很难表达出非常准确的合约,有时候事情会出乎意料。下面的代码让一些人感到惊讶:
struct S { int *x; }; int main() { int n = 7; const S s = {&n}; // 不要触碰s,它是只读的! *s.x = 666; // 等等,什么?s是const!撒旦参与了吗? }
我相信你很自然地知道为什么上面的代码可以编译。你不能修改指针的值(它指向的地址),而不是它后面的值。你必须承认有些人会对此感到困惑。
我不知道这是否有任何意义,但我一直在使用C++中的const。非常准确。对此感到疯狂。不确定它是否曾经救过我的命,但是在转向Go之后,我必须承认我从未想念过它。考虑到所有这些边界情况和异常,我真的相信像Go这样的极简主义语言的创建者会决定跳过这个特性。
> 类型安全是指具有允许速度和防止与类型相关的错误的合约。
同意。例如,在Go中,我喜欢类型之间没有隐式转换。这确实防止了我遇到_与类型相关的错误_。
> 另一个具有这种功能的特性是const限定符。
根据我整个回答 - 我不同意。一个普通的const合约肯定可以做到这一点,但一个简单的const限定符是不够的。然后你需要一个mutable
的限定符,也许是一种const_cast的特性,但仍然 - 它可能会让你产生错误的保护信念,因为很难理解什么是真正的常量。
希望一些语言的创建者能够设计出一种完美的方式来定义我们代码中的常量,然后我们将在Go中看到它。或者转向新的语言。但就个人而言,我不认为C++的方式是特别好的。
(另一种选择是遵循函数式编程范式,它们希望看到所有的“变量”都是不可变的。)
英文:
Please treat it as an expanded comment. I'm not any programming language designer, so can't go deep inside the details here, but will present my opinion as a long-term developer in C++ and short-term developer in Go.
Const is a non-trivial feature for the compiler, so one would have to make sure whether it's providing enough advantage for the user to implement it as well as won't sacrifice the simplicity of syntax. You might think it's just a const
qualifier we're talking about, but looking at C++ itself, it's not so easy – there're a lot of caveats.
-
You say
const
is a contract and you shouldn't be able to modify it at any circumstances. One of your arguments against using read only interfaces is that you can cast it to original type and do whatever you want. Sure you can. The same way you can show a middle finger to the contract in C++ by usingconst_cast
. For some reason it was added to the language and, not sure I should be proud of it, I've used it once or twice. -
There's another modifier in C++ allowing you to relax the contract –
mutable
. Someone realised thatconst
structures might actually need to have some fields modified, usually mutexes protecting internal variables. I guess you would need something similar in Go in order to be able to implement thread-safe structures. -
When it comes simple
const int x
people can easily follow. But then pointers jump in and people really get consfused.const int * x
,int * const x
,const int * const x
– these are all valid declarations ofx
, each with different contract. I know it's not a rocket science to choose the right one, but does your experience as a senior C++ programmer tell you people widely understand these and are always using the right one? And I haven't even mentioned things likeconst int * const * * * const * const x
. It blows my mind.
Before I move to point 4, I would like to cite the following:
> Worse, you can pass an object by value, but if it contains a pointer,
> you can actually modify its contents
Now this is interesting accusation. There's the same issue in C++; worse – it exists even if you declare object as const, which means you can't solve the problem with a simple const
qualifier. See the next point:
-
Per 3, and pointers, it's not so easy to express the very right contract and things sometimes get unexpected. This piece of code surprised a few people:
struct S { int *x; }; int main() { int n = 7; const S s = {&n}; // don't touch s, it's read only! *s.x = 666; // wait, what? s is const! is satan involved? }
I'm sure it's natural for you why the code above compiles. It's the pointer value you can't modify (the address it points to), not the value behind it. You must admit there're people around that would raise their eyebrow.
I don't know if it makes any point, but I've been using const in C++ all the time. Very accurate. Going mental about it. Not sure whether is has ever saved my ass, but after moving to Go I must admit I've never missed it. And having in mind all these edge cases and exceptions I can really believe creators of a minimalistic language like Go would decide to skip on this one.
> Type-safety is about having a contract that allows speed and prevents
> type-related bugs.
Agreed. For example, in Go, I love there're no implicit conversions between types. This is really preventing me from type-related bugs.
> Another feature that does this too is the const qualifier.
Per my whole answer – I don't agree. Where a general const contract would do this for sure, a simple const qualifier is not enough. You then need a mutable
one, maybe kind of a const_cast feature and still – it can leave you with misleading believes of protection, because it's hard to understand what exactly is constant.
Hopefully some language creators will design a perfect way of defining constants all over in our code and then we'll see it in Go. Or move over to the new language. But personally, I don't think C++'s way is a particularly good one.
(Alternative would be to follow functional programming paradigms, which would love to see all their "variables" immutable.)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论