Functinality like in C for pointers

In C we can make a function what doesn’t change a value what a pointer from parameters point to:

void f(const char *str, int len);

So, we see that function can’t change data. It is very useful feature, because we can pass to function a pointer without copying data, and a function header tell us that it doesn’t change data. Can we do like this in Go?

From this thread:

Go doesn’t have anything like C++'s const for methods.
Const used in this way is a C++ invention, and like most
C++ inventions, it does have its benefits, but it also carries
a lot of baggage. Somehow most other languages manage
fine without it, and it seems likely that Go will too.

Russ

And also this:

Well, it does seems likely that I personally don’t value it as much as
you do. There is a cost-benefit tradeoff to all language decisions.
The const qualifier in C/C++ carries a real cost. Ultimately the
language designers have to weigh the cost and benefits and make a
decision. (And, to be clear, my personal input in the language spec
was relatively small.)

I think the use case where you have data that needs to be immutable AND passed by reference due to size is something that has been decided shouldn’t be a language feature. So in most cases I think people would just pass this string as a value. In terms of package design where the package is meant to be consumed by some other developer: if you want to mutate data from my package in your methods, go for it.

1 Like

Ok) thank you for the answer.
Using immutable data seems very useful to me. It can do code more understandable. I found out that there is a package named “immutable”. Is it used by somebody in practice?

I can’t speak to how much that package is being used (I don’t use it and haven’t seen it anywhere). When I see a pointer being passed around in Go, I usually assume one of three things:

  1. It’s going to modify my variable.
  2. It’s assuming that whatever variable being passed is going to be very large.
  3. It’s indicating that the variable can be nil.

If you want to make which of those things really obvious I would probably just use documentation personally. So in the case of a large string:

// LargeString will do the thing with the large string. Using a pointer 
// because it is assumed v is large. Will not mutate v. Pinky promise.
func LargeString(v *string) { }

And let’s say I have the example where I am going to mutate v in some way:

// MutateString will add "all your base" to the end of v.
func MutateString(v *string) { }

And finally the case where we are using a pointer to differentiate between nil and empty string:

// NilString will print important information to the console.
// A nil value will print "not logged in" but empty string will
// print "unknown user".
func NilString(v *string) { }

The idea here being that we are communicating/documenting the intent. I don’t program C but my understanding is that the behavior you are describing above is not perfectly safe either.

Another option might be something like this:

// Immutable represents an immutable value
type Immutable[T any] struct {
	value T
}

// Get the stored value
func (i Immutable[T]) Value() T {
	return i.value
}

// Create a new immutable value
func New[T any](v T) Immutable[T] {
	return Immutable[T]{value: v}
}

Not perfect but it does, once again, indicate some sort of intent. You could take a look at this article for more ideas.

1 Like

I think documentation is a good and simple choice. Maybe, there are some circumstances when we need to do more.