Different package name makes the different result

Different package name makes the different result, when I use package app, the result is

Student study
Student study

when I change the package name from app to stu, the result is

BoyStudent study
GirlStudent study

I want the second result but unaffected for the package name

package app

import (
	"fmt"
)

type IStudent interface {
	study()
}

type Student struct {
}

func (me *Student) study() {
	fmt.Println("Student study")
}

type StudentProxy struct {
	student IStudent
}

func (me *StudentProxy) study() {
	me.student.study()
}

type studentManager struct {
	students map[string]*StudentProxy
	finder   func(string) IStudent
}

var sm = &studentManager{
	students: map[string]*StudentProxy{},
}

func StudentManager() *studentManager {
	return sm
}

func (me *studentManager) SetFinder(finder func(string) IStudent) {
	me.finder = finder
}

func (me *studentManager) GetStudent(name string) *StudentProxy {
	proxy, ok := me.students[name]
	if !ok {
		stu := me.finder(name)
		proxy = &StudentProxy{student: stu}
		me.students[name] = proxy
	}
	return proxy
}

func (me *studentManager) MethodA() {
	names := []string{"boy", "girl"}
	for _, name := range names {
		me.GetStudent(name).study()
	}
}

func (me *studentManager) MethodB() {
	names := []string{"boy", "girl"}
	for _, name := range names {
		me.GetStudent(name).study()
	}
}
package auto

import (
	"github.com/boringwork/godemo/interface/app"
	"github.com/boringwork/godemo/interface/impl"
)

func Finder(name string) app.IStudent {
	switch name {
	case "boy":
		return &impl.BoyStudent{}
	case "girl":
		return &impl.GirlStudent{}
	}
	return nil
}
package impl

import (
	"fmt"

	"github.com/boringwork/godemo/interface/app"
)

type BoyStudent struct {
	app.Student
}

func (me *BoyStudent) study() {
	fmt.Println("BoyStudent study")
}

type GirlStudent struct {
	app.Student
}

func (me *GirlStudent) study() {
	fmt.Println("GirlStudent study")
}
package main

import (
	"github.com/boringwork/godemo/interface/app"
	"github.com/boringwork/godemo/interface/auto"
)

func main() {
	app.StudentManager().SetFinder(auto.Finder)
	app.StudentManager().MethodA()
}

What version of Go are you using (go version)?

go version go1.10.1 darwin/amd64

You have some conflicting information in your post that makes it hard to follow / understand what the problem is. At the top of the post you say package app results in:

Student study
Student study

Yet in your screenshot you say package app gives you:

BoyStudent study
GirlStudent study

Would you be able to share a working repo with instructions on how to reproduce? That would make it easier to understand what’s happening.

Yes, you are right, I checked again and tried more package names. The results are as follows

I still don’t have enough info to know what’s going on. If you’re not able to share the source repo with instructions on how to reproduce, are you able to provide diffs at each step?

I just submitted the demo code to github:

I found new question, keep package name, and rename method name from study to Study, have different result too.

This looks to be related to the fact your methods aren’t exported. You generally can’t invoke unexported (lowercased) methods or functions from other packages. As soon as I changed the methods, and interface, to use Study instead of study it started to work as I expected.

It seems like the compiler takes in to account whether the method is exported or not when deciding which to invoke. So in short, if you’re exporting an interface outside of a package you should also export all of the methods needed to implement said interface.

Let me know if switching study to Study fixes it for you.

It looks like you’re trying to treat struct embedding like inheritance, but “overriding” one of the methods of the embedded type. Struct embedding is not inheritance, and may result in odd behaviors like this. In this case, it seems like the app.Student type shouldn’t have a Study method.

Yes, use Study can be work now, but i’m not sure it will always work in any other condition, I will always pay attention to this question

As you said, i’m trying to treat struct embedding like inheritance, since golang provides such a feature, it should be used. Even if it cannot be used as inheritance, there should be no uncertainties in computer, either all cases can be accessed, or none can be accessed, instead of being affected by the package name and producing different results

Let me know is it a issue for language or not, and how will golang handle it.

Thand you very much.

Per the things I’ve seen, it should work a little more reliably when you export the function by capitalizing it. If you unexport it, and work with an exported interface, it seems like the behavior is variable.

To your point about consistency, that isn’t always the case. Languages have undefined or unexpected behaviors all of the time. I’d argue that having an unexported method defined in an exported interface is a behavior that’s allowed but may result in undefined behaviors. It’s also a pattern indicating the code isn’t written as thoughtfully as it could.

Should it be disallowed and result in a compiler error? Maybe, that’s hard to say as some may depend on it and never hit this kind of issue.

I don’t know if I agree that struct embedding should be used because the language has it. The Go language allows you to redefine what nil means in the current scope, but that doesn’t make it a good idea:

I never use struct embedding in my programs, likely because I also don’t write my Go code like it’s an OOP language. Coming from a Ruby background I wanted things like inheritance, but I’ve learned I rarely need it.

It looks like it’s a bug. I recreated a minimal example. If you rename the folder “child” to “achild”, then Child will be printed, if folder name is “child” than just Base will be. It turns out that Go imports packages in alphabetical order.

scratch/base/base.go


import "fmt"

type Interface interface {
	do()
}

type Base struct{}

func (Base) do() { fmt.Println("base") }

func Call(f func() Interface) {
	f().do()
}

scratch/child/child.go


import "scratch/base"
import "fmt"

type Child struct{ base.Base }

func (Child) do() { fmt.Println("child") }

scratch/main.go


import "scratch/child"     // prints "base"
//import "scratch/achild"  // prints "child"
import "scratch/base"

func main() {
	base.Call(func() base.Interface {
		return child.Child{}
	})
}

Which is documented in the spacs (Package Initialisation:

To ensure reproducible initialization behavior, build systems are encouraged to present multiple files belonging to the same package in lexical file name order to a compiler.

1 Like

Yes, but if I make do() public it is always display “child”.