Refered to un-exported type by type inference – Bug or feature?

Hi folks,

suppose we use the following simple package …

    package loadme

    type myStruct struct {
    	Number int
    	Text   string
    }

    func ReturnMyStructType() myStruct {
    	var a myStruct = myStruct{123, "abc"}
    	return a
    }

Then, this code is valid, but it shouldn’t!

    import (
    	"fmt"
    	"myProjekt/loadme"
    )

    func main() {
    	b := loadme.ReturnMyStructType()
    	fmt.Println(b)
    }

In my opinion, the example should be invalid, because b now gets unexported type loadme.myStruct . But please check it out, it works!

Please compare with this invalid code

   // ./main.go:13:8: cannot refer to unexported name loadme.myStruct
   var c loadme.myStruct
   c = loadme.ReturnMyStructType()
   fmt.Println(c)

Is that behaviour a bug or a feature? (I’m, using go1.11.4)

It’s a feature. An exported function like yours can return an unexported struct (or other types) as well.

However, when you make one of the fields unexported, the other package can’t access them directly:

type myStruct struct {
  number int     // unexported
  Text   string  // exported
}

For example, main.go can’t do this anymore:

b := loadme.ReturnMyStructType()
fmt.Println(b.number)

But it can do this:

b := loadme.ReturnMyStructType()
fmt.Println(b.Text)

Hi inancgumus,

thanks a lot for your answer! However, I’m not convinced, that this inconsistent behaviour is a feature. Because you can intentionally achieve the same effect by making the struct “public” by uppercasing the first letter …

    type MyStruct struct {
      number int     // still unexported
      Text   string  // exported
    }

… but with the difference, that in this case, following code is possible and valid:

import (
	"fmt"
	"myProjekt/loadme"
)

func main() {
	var b loadme.MyStruct
	b = loadme.ReturnMyStructType()
	fmt.Println(b)
}

while

import (
	"fmt"
	"myProjekt/loadme"
)

func main() {
	var b loadme.myStruct
	b = loadme.ReturnMyStructType()
	fmt.Println(b)
}

must be invalid! Thats the reason, why

import (
	"fmt"
	"myProjekt/loadme"
)

func main() {
	b: = loadme.ReturnMyStructType()
	fmt.Println(b)
}

should be invalid too, with …

    type myStruct struct {
      number int     // still unexported
      Text   string  // exported
    }

Here a good article about this behaviour.

The keyword here is: Identifiers.

Exporting is all about whether you can directly access the identifiers from another package or not. It’s all about the identifiers. It’s not about preventing the usage of those indirectly (like printing an unexported struct etc), because your exported function returns it (because your function has an access to those identifiers).

This is valid because you’re not accessing to any unexported identifiers:

// Here Go infers the type of the `b` variable as `loadme.myStruct`, 
// and loads it into the `b` variable.
//
// But you can't "access" myStruct by its "name" (its identifier: myStruct)
b := loadme.ReturnMyStructType()

This is invalid because you’re trying to access to an unexported identifier: myStruct:

var b loadme.myStruct

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.