英文:
Return an unexported type from a function
问题
从导出的函数中返回未导出的类型是否被认为是不好的风格?当我使用它时,我发现它主要会引发问题。
一个更好的问题可能是:什么时候从导出的函数中返回未导出的类型是一个好主意。
英文:
Is it considered bad style to return an unexported type from an exported function?
When I've used it, I mostly find it just creates problems.
A better question might be: when is it a good idea to return an unexported type from an exported function.
答案1
得分: 30
我认为从一个导出的函数中返回一个未导出的变量没有问题。根据定义,这就是一个访问器。
不过,我只会在需要每次访问未导出变量时执行一些逻辑的情况下这样做。
编辑:
我希望我理解了你明确的问题。
如果你有一个未导出的type user struct{}
,并且你用NewUser()
返回它,那么这是否符合你的用例?如果是的话,那就是一种工厂设计模式,在Go语言中非常有用,如果你不希望第三方开发者直接创建user
类型的对象。这样,你的“构造函数”或“工厂”就是获取新实例的唯一位置。
那么,这是否是“不好的风格”?我认为这取决于需要克服的挑战是什么。
英文:
I would say there's nothing wrong with returning an unexported variable from an exported function. That's what an accessor is by definition.
That said, I would only do that in the case where there was some logic that needed to occur every time the unexported variable needed to be accessed.
EDIT:
I hope I understand your clarified question.
If you have an unexported type user struct{}
and you return it with NewUser()
, would that fit your use case? If so, then that is a factory design pattern and is useful in Go if you do not want a 3rd party developer to directly create a user
type object. That way, your "constructor" or "factory" is the only place to get new instances.
So, is it "bad style"? I'd say it depends on what challenge needs to be overcome.
答案2
得分: 13
Golang的代码检查工具在返回未导出类型时会发出警告,所以我认为尽管在技术上是可能的,但也不鼓励这样做。
其中一个原因是,一些接收未导出类型的代码无法在不将其作为接口类型(例如:interface{}或其他类型)进行类型化的情况下利用它进行各种“签名”(例如:类型、函数)。
例如:
package people
type me string
func NewMe() me {
return "me"
}
// in main.go
package main
type eff struct {
m *people.me // <-- 无法这样做
}
func main() {
var m people.me // <-- 无法这样做
m2 := people.NewMe() // 可以这样做
}
英文:
Golang's linters return a warning when you return an unexported type, so I'd say though it's technically possible it's also to be discouraged. 1
One reason is that some code that receives the unexported type cannot utilise it for various "signatures" (eg: types, funcs), without typing it as an interface (eg: interface{} or otherwise)
eg:
package people
type me string
func NewMe() me {
return "me"
}
// in main.go
package main
type eff struct {
m *people.me // <-- cant do this
}
func main() {
var m people.me // <-- cant do this
m2 := people.NewMe() // can do this
}
答案3
得分: 6
我认为值得为其他遇到这个问题的人提供另一个观点。更好的解决方法是,如果用户需要以任何方式与其交互,要么为该类型定义一个实际的导出接口,要么不返回未导出的类型,这样就不必处理这种情况。这主要是一个设计问题,让我来解释一下。
虽然从技术上讲,可以返回一个未导出的类型,就像上面所示的那样,但是该类型必须被赋值给interface{}
,以供该包之外的代码使用。对我来说,这是一种代码异味,我在代码审查中会对此表示怀疑。同样地,我会对接受未导出参数的导出函数/方法提出质疑。
我对此的最大问题是函数签名没有清楚地传达其使用范围。它是一个内部函数,因为它返回一个内部类型吗?它是为外部消费者调用的吗,因为它是一个导出函数?这真的很模糊。
此外,虽然返回interface{}
使得该类型可传输,但它也在一定程度上破坏了类型安全性。如果返回interface{}
的目的是防止用户在将数据传递给API的另一部分之前更改数据,那么第二个API需要接受interface{}
并在使用之前检查有效类型。这也是一种代码异味,编译器无法帮助我们处理错误的调用。
英文:
I think it's worth posting another viewpoint for others that happen upon this question. A better way to address this is to either define an actual exported interface for that type if the user is meant to interact with it in any way, or just don't return unexported types so you won't have to address this situation. This is primarily a design problem, let me explain.
While it's technically possible to return an unexported type, as shown above, the type must be assigned to interface{}
for code outside of that package. That to me is a code smell and is something I would seriously question in a code review. Similarly, I would question an exported function/method accepting unexported parameters.
The biggest problem I have with this is that the function signature does not clearly convey its scope of use. Is it an internal function since it returns an internal type? Is it meant to be called by outside consumers since it's an exported function? It's really ambiguous.
Plus, while returning an interface{}
makes this type transferable, it also breaks type safety to a certain degree. If the purpose of returning interface{}
is to prevent the user from changing data before passing it to another part of the API, then the secondary API needs to then accept interface{}
and check for a valid type before using it. Also code smelly and the compiler can't help us with bad calls here.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论