Go没有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.

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.

  1. 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 using const_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.

  2. There's another modifier in C++ allowing you to relax the contract – mutable. Someone realised that const 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.

  3. 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 of x, 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 like const 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:

  1. 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.)

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.