-3

I understand from the Go tour that upper case variables are meant to be exported, while lower case variables in a struct are not exported. But I am trying to achieve something else.

In Java, I could define a class Adult, like so

class Adult
{
    private int age;

    void setAge(int x)
    {
        if (x >= 18)
            this.age = x;
    }
}

In GoLang, I understand I could do something similar like so:

//adult.go
package main

type Adult struct {
    age int //Made this lowercase, therefore private. I expect no one messes with this.
}

func (a *Adult) setAge(x int) { //Setter only
    if x >= 18 {
        a.age = x
    }
}

However, I can do this in my main.go:

package main

import "fmt"

func (a *Adult) getAge() *int { // This shouldn't be possible?
    return &a.age
} 

func main() {
    var temp *Adult = new(Adult)
    temp.setAge(24)
    fmt.Println(*temp.getAge())

    var this_age_should_be_hidden = temp.getAge() //How do I prevent someone from doing this?
    *this_age_should_be_hidden = 5
    fmt.Println(*temp.getAge())
}

That brings me to the title of this question. If we lower case our variables, what is the point? Can we always define methods on the struct which would expose unexported variables? Doesn't that defeat the purpose of lowercasing it, in the first place?

Also, what would be the Go way to achieve what I have done in the Java version above? How can I ensure that certain fields of my structs are not set to invalid values outside the class?

shikharraje
  • 127
  • 1
  • 17
  • 6
    Unexported identifiers are available throught the **package** in which they were declared. Exported/Unexported is a package-level concept. Not type-level, block-level, file-level, or whatever-else-level. Package-level. – mkopriva Oct 13 '21 at 12:33
  • 3
    i.e. a package that *imports* the package in which `Adult` is declared cannot reference `age` and it cannot reference `getAge` (since that's lowercase as well). – mkopriva Oct 13 '21 at 12:35
  • 1
    If you return a pointer to a variable in Java, can't you change that address's value also? – colm.anseo Oct 13 '21 at 12:36
  • @colm.anseo In this example, I don't think so. Firstly, if no one edits the `Adult.java` file, I know that no one is returning `age` for sure. Secondly, I think `age` can be exported (by a getter), but it cannot be modified to an invalid value, like it's happening in Go. It's an Integer, so ```Adult a = new Adult(18); x = a.getAge(); x = 5; y = a.getAge() // Would still be 18``` – shikharraje Oct 13 '21 at 12:40
  • 2
    Your Go pointer example is contrived - but you won't allow the same contrived experiment in Java? – colm.anseo Oct 13 '21 at 12:44
  • 3
    @shikharraje if your getters are returning pointers to unexported fields, then they are effectively allowing any client to modify the values of the unexported field. The getters should be returning the fields' values, not the pointers to them. – mkopriva Oct 13 '21 at 12:44
  • @mkopriva What if make `getAge` as public method as `GetAge`, while still keep `age` private? – rustyhu Oct 13 '21 at 12:45
  • 2
    @rustyhu if your question is aimed at my 3rd comment then the answer is: it doesn't matter, the returned pointer allows you to modify the field regardless of whether or not the field is exported. – mkopriva Oct 13 '21 at 12:53
  • 4
    "How can I ensure that certain fields of my structs are not set to invalid values outside the class?" You cannot and this is fine as this doesn't lead to problems in real life. Just get used to it. – Volker Oct 13 '21 at 13:08
  • I think between the comments from @mkopriva and @Volker , I have an answer. I had drawn an incorrect parallel between Java's `private` and Go's lowercase variables. Thanks for pointing out that it's a package level concept, and that there is no way to ensure that fields of structs are not set to invalid values outside it. If you guys could put that in an answer, I can mark it and close this. – shikharraje Oct 13 '21 at 15:26
  • Or, I **just** saw this in SO's related questions section: https://stackoverflow.com/a/16657370/1243462 My question can be closed as an exact duplicate. I had the same thought process as the OP there, but I think I worded my question differently, and so I didn't find that question before. – shikharraje Oct 13 '21 at 15:45
  • The difference between my question and the above linked one is that, I thought receiver functions could be used anywhere to access "private" variables of a struct, while there, the OP define the pointer-returning function in the same file. – shikharraje Oct 13 '21 at 15:49

1 Answers1

3

Private fields will be available within the package.

Consider the following, based on your example, but placing Adult inside its own person package :

package person

type Adult struct {
    age int //Made this lowercase, therefore private. I expect no one messes with this.
}

func (a *Adult) setAge(x int) { //Setter only
    if x >= 18 {
        a.age = x
    }
}

Should an adult be created inside person package, one could set his age. However, one would not be able to set the age from an external package :

package main

func main() {
    var a person.Adult{}
    a.setAge(10)         //impossible - method is private
}

If you need to use setters, you might want to capitalize them to open them to external packages so that you can do :

package main

func main() {
    var a person.Adult{}
    a.SetAge(10)        //available method
}

Ado Ren
  • 3,511
  • 4
  • 21
  • 36