6

Let's say I have the following hierarchy for my project:

fragment/fragment.go
main.go

And in the fragment.go I have the following code, with one getter and no setter:

package fragment

type Fragment struct {
    number int64   // private variable - lower case
}

func (f *Fragment) GetNumber() *int64 {
    return &f.number
}

And in the main.go I create a Fragment and try to change Fragment.number without a setter:

package main

import (
    "fmt"
    "myproject/fragment"
)

func main() {
    f := new(fragment.Fragment)

    fmt.Println(*f.GetNumber()) // prints 0

    //f.number = 8 // error - number is private

    p := f.GetNumber()
    *p = 4                      // works. Now f.number is 4
    fmt.Println(*f.GetNumber()) // prints 4
}

So by using the pointer, I changed the private variable outside of the fragment package. I understand that in for example C, pointers help to avoid copying large struct/arrays and they are supposed to enable you to change whatever they're pointing to. But I don't quite understand how they are supposed to work with private variables.

So my questions are:

  1. Shouldn't the private variables stay private, no matter how they are accessed?
  2. How is this compared to other languages such as C++/Java? Is it the case there too, that private variables can be changed using pointers outside of the class?

My Background: I know a bit C/C++, rather fluent in Python and new to Go. I learn programming as a hobby so don't know much about technical things happening behind the scenes.

user31208
  • 1,448
  • 1
  • 18
  • 22
  • 2
    A pointer is a reference to a place in memory. If you return a pointer to a "private" variable, it still points to that very variable; the caller can still change the pointer. If you like to impose a "no alteration" restriction, consider returning the object itself instead of just a copy. – fuz May 20 '13 at 19:57
  • I recently asked a similar question: https://stackoverflow.com/q/69555629/1243462 . Basically, there's an additional complication where the `GetNumber()` method that you've defined, is not even defined by you. A collaborator would inadvertently add a new file in the same package, and add that as a receiver method on the Fragment struct! In my example, I faced a situation where I had defined a struct with an integer field that could only take certain values. A colleague did exactly this and suddenly, the struct was holding invalid values. – shikharraje Oct 13 '21 at 15:53

1 Answers1

8

You're not bypassing any access privilegies. If you acquire a *T from any imported package then you can always mutate *T, ie. the pointee at whole, as in an assignment. The imported package designer controls what you can get from the package, so the access control is not yours.

The restriction to what's said above is for structured types (structs), where the previous still holds, but the finer granularity of access control to a particular field is controlled by the field's name case even when referred to by a pointer to the whole structure. The field name must be uppercase to be visible outside its package.

Wrt C++: I believe you can achieve the same with one of the dozens C++ pointer types. Not sure which one, though.

Wrt Java: No, Java has no pointers. Not really comparable to pointers in Go (C, C++, ...).

Dominik Honnef
  • 17,937
  • 7
  • 41
  • 43
zzzz
  • 87,403
  • 16
  • 175
  • 139
  • 2
    This is exactly right. Access isn't about mutation--it's about visibility. You can't *see* frag.number outside the package--accessing it is illegal; however, if fragment gives you a pointer that happens to point to frag.number, then you're free to modify it. This is exactly true in C++. – weberc2 May 20 '13 at 20:19
  • This is also exactly how it works in Python, albeit [in Python] everything is a "pointer" by default. – weberc2 May 20 '13 at 20:20
  • It's also possible to leak private implementation details out of a Java class. Careless use of arrays is the normal route to this. – Rick-777 May 22 '13 at 18:35