Here is an example, where the non-internal function Foo can expose the internal struct Bar:
$ cat foo/internal/bar/bar.go
package bar
type Bar struct {
X int
}
$ cat foo/foo.go
package foo
import "foo/internal/bar"
func Foo() bar.Bar {
return bar.Bar{X: 42}
}
The compiler does not complain about this when using the return value of Foo in another Go module. Why is it so? Is this one of these situations where the Go authors give us “a sharp knife” for special occasions, assuming that we developers know not to misuse it?
Go has a model that everything written with the capital letter is exported. No matter if you call your package internal. If you don’t want someone to get access to the internals leave only api and make everything none exported
Yes I am aware. And I guess you need to read this article again. It clearly states that internal can be imported in any package rooted to the internal. In your example you do exactly this. And this means if your internal things are exported, then you can get access to them. Even in GitHub issue there are lots of links to discussions about misunderstanding around internal name. cmd/go: modules: exposure of internal packages · Issue #30569 · golang/go · GitHub
I’m sorry, I think we’re getting off on the wrong foot here. I’m well aware that the foo package should be able to use the foo/internal/bar package. This is working as intended.
What surprised me was, that I was able to use the the Bar struct of the internal package of the foo module in another Go module. For example, in a different Go module, I was able to access the Field of the internal struct:
$ cat baz/baz.go
package main
import "fmt"
import "foo"
func main() {
fmt.Println(foo.Foo().X)
}
$ cd baz && go run .
42
Your question is very strange, it’s similar to this:
type b struct {
}
func FuncA() *b {
return &b{}
}
Don’t limit yourself to features. Closures are just to isolate something from being called externally, but that doesn’t mean that the things thrown can’t be used.