Invoking struct function gives "cannot refer to unexported field or method"
Asked Answered
R

2

54

I have a structure something like this:

type MyStruct struct {
    Id    string
}

and function:

func (m *MyStruct) id() {
   // doing something with id here
}

Also I have another structure like this:

type MyStruct2 struct {
    m *MyStruct
}

Now I have a function:

func foo(str *MyStruct2) {
    str.m.id()
}

But I'm getting error in compile time:

str.m.id undefined (cannot refer to unexported field or method mypackage.(*MyStruct)."".id

How can I call this function correctly?

Rural answered 30/6, 2014 at 10:22 Comment(0)
W
109

From http://golang.org/ref/spec#Exported_identifiers:

An identifier may be exported to permit access to it from another package. An identifier is exported if both:

  1. the first character of the identifier's name is a Unicode upper case letter (Unicode class "Lu"); and
  2. the identifier is declared in the package block or it is a field name or method name.

So basically only functions / variables starting with a capital letter would be usable outside the package.

Example:

type MyStruct struct {
    id    string
}

func (m *MyStruct) Id() {
   // doing something with id here
}

//then

func foo(str *MyStruct2) {
    str.m.Id()
}
Whetstone answered 30/6, 2014 at 10:36 Comment(0)
I
9

If you change MyStruct.Id to MyStruct.id:

  • You'll no longer be able to access it to initialize MyStruct2.
  • id will be accessible only through its own package: the first package.
  • Because: MyStruct and MyStruct2 are in different packages.

To solve the problem you can do this:

Package first:

package first

type MyStruct struct {
    // `id` will be invisible outside of `first` package
    // because, it starts with a lowercase letter
    id string
}

// `Id()` is visible outside to `first` package 
// because, it starts with an uppercase letter
func (m *MyStruct) Id() string {
  return m.id
}

// Create a constructor function to return `*MyStruct`
func NewMyStruct(id string) *MyStruct {
    return &MyStruct{
        id: id,
    }
}

Package second:

package second

// Import MyStruct's package
import "first"

type MyStruct2 struct {
    // If you don't use `m` here as in your question, 
    // `first.MyStruct` will be promoted automatically.
    //
    // So, you can reach its methods directly, 
    // as if they're inside `MyStruct2`
    *first.MyStruct
}

// You can use `Id()` directly because it is promoted
// As if, inside `MyStruct2`
func foo(str *MyStruct2) {
    str.Id()
}

// You can initialize `MyStruct2` like this:
func run() {
    foo(&MyStruct2{
        MyStruct: first.NewMyStruct("3"),
    })
}
Immunochemistry answered 25/6, 2017 at 12:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.