-
Notifications
You must be signed in to change notification settings - Fork 18.1k
godoc should show Exported methods for unexported types #2273
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Labels
Comments
I don't believe this is common. It never comes up in the Go standard library. In what code is this common? This scenario is incomplete. It does not show any way for a caller to create an unexported. Without that, there is no point to showing it in godoc. If the scenario did have a way, like: func NewUnexported() unexported then I would suggest making it instead func NewUnexported() Exported If you are not willing to let others use the name ("unexported"), then you shouldn't force it on them in the public API. Among other problems, you can't declare a variable that will process the result. You can write x := p.NewUnexported() but not var x p.unexported = p.NewUnexported() That's not good API design. The various 32-bit hash functions are a good example of this in practice: hash/adler32's New returns a hash.Hash, not a *digest. There's no need to expose the internal name in the external API. Whether it's internals can be accessed depends on whether the field names are upper or lower case. It does not depend on the struct name. Russ |
My original note was too terse and assumed a New() function but didn't put one in. And the pattern is clearly common since it is exactly what is done in hash/adler32: an unexported struct (digest) and and exported API (hash.Hash). In adler32 the unexported digest struct has many public methods, Reset, Size, Write, Sum, and Sum32. But none of these show up in the documentation, and this is what I'm suggesting is a bug. The situation I have is essentially this: type x struct { i int f func(int) bool } func NewX(i int, f func(int) bool) *x { return &x{i, f} } func (x *x) Check(y int) bool { return x.f(y) } The x type must not be created using var a x or a := &x{} because that would leave f at nil which is not acceptable for this type. So my solution is to force the user to use a New function and to make the x type unexported. This way only valid x's can be created. If I need to be able to pass an x I can create an interface: type Xer interface { Check(int) bool } and now I can pass Xer's around and the internals of x are not exposed anywhere. However, the *documentation* for x's methods do not appear in godoc. (Just as the documentation for adler32's digest's exported method's docs don't appear.) Or are you saying that this pattern is wrong and that it should be done differently? (And if so, why doesn't hash/adler32 do it differently?) |
This is different from what hash/adler32 does. Here you have used an unexported name in an exported API. The answer is not to do that, precisely because it puts the caller in a weird straightjacket where the type cannot be mentioned. You are fighting the language. Zero values happen. Good solutions include: (1) define Xer somewhere and make NewX return Xer instead of *x (this is what hash/adler32 does). (2) document that the zero value is not valid and maybe provide an Init method (this is what tabwriter.Writer does). Taking away the caller's ability to speak about the return type - which is what returning *x does - is not a good solution. Russ |
Yes and no. adler32.New returns a hash.Hash32, and that interface is documented (godoc hash Hash; godoc hash Hash32). There should be a cross-link in the HTML straight to it, but we're not there yet (we'll get there). It is true that if there are things specific to this implementation that need to be mentioned, they cannot be documented on the digest.Write method, because that method doesn't show up in docs. But a prospective user of the package doesn't know what digest is anyway, since it is unexported, so showing method docs on it wouldn't be a good user experience. Instead the usual approach is to document the function that is returning the interface value. So the adler32.New doc says not just that it returns a Hash32 but that it returns a Hash32 computing the Adler-32 checksum. I just noticed that compress/zlib demonstrates both choices: NewReader returns an io.ReadCloser and describes that the ReadCloser is a decompressing reader. NewWriter returns a concrete *Writer, because it needed to add the non-standard Flush method and associated documentation. If you were to declare var w zlib.Writer then using w would crash. That's okay. We trust programmers either not to do that or to accept the consequences. Russ |
Glad to have this discussion here in an issue to point at. I can't find the one from the last time it came up. Owner changed to @rsc. Status changed to WorkingAsIntended. |
I think this was the last time this issue came up. Godoc output is inconsistent (compare md5 and blowfish packages) - golang-nuts http://groups.google.com/group/golang-nuts/browse_thread/thread/5e1709381889e3d5/030f051e33ee769d |
Issue #3808 has been merged into this issue. |
Issue #4213 has been merged into this issue. |
This was referenced Dec 8, 2014
This issue was closed.
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
The text was updated successfully, but these errors were encountered: