英文:
Go function types that return structs being used with interfaces
问题
我有一个在包中的结构体,它有耗时的方法,并且通过它的工厂函数构造也很耗时。因此,在依赖这个结构体的包中,我希望能够使用一个伪造的工厂函数和一个伪造的结构体来进行测试。由于结构体是通过工厂函数构造的,我希望在测试时伪造工厂函数,并在我的结构体中传入一个替代的工厂函数。
一个昂贵包的示例代码如下:
package expensive
import "fmt"
type myStruct struct{}
func (m *myStruct) DoSomething() {
fmt.Println("In Do something")
}
func (m *myStruct) DoSomethingElse() {
fmt.Println("In do something else")
}
// CreateInstance is expensive to call
func CreateInstance() *myStruct {
return &myStruct{}
}
使用这个包的主要包如下:
package main
import "play/expensive"
func main() {
thing := structToConstruct{expensive.CreateInstance}
thing.performAction()
}
type myInterface interface {
DoSomething()
}
type structToConstruct struct {
factoryFunction func() myInterface
}
func (s *structToConstruct) performAction() {
instance := s.factoryFunction()
instance.DoSomething()
}
然而,这段代码报错:
.\main.go:6: cannot use expensive.CreateInstance (type func() *expensive.myStruct) as type func() myInterface in field value
然而,*expensive.myStruct
确实实现了myInterface
接口,所以我不明白为什么Go会对这个设置的类型安全性报错。
我后来意识到,在我的主方法中,我可以像这样包装我的函数:
wrapper := func() myInterface {
return expensive.CreateInstance()
}
thing := structToConstruct{wrapper}
这样就可以工作了,但我仍然不太明白为什么当一个函数期望返回该接口的实例时,我不能使用实现该接口的结构体,尤其是在这个修复中不需要进行任何类型断言/转换,因为它只是调用底层的工厂函数。
编辑:我后来看到了这个向语言中添加此功能的提案。该提案被拒绝了:
https://github.com/golang/go/issues/12754
英文:
I have a struct in a package that has time consuming methods on it and is also time consuming to construct via it's factory function. Therefore, in the package that depends on this other struct I would like to be able to test it using both a fake factory function and a fake struct once it has been created. As the struct is constructed via a factory function I would like to fake the factory function and pass an alternative factory function into my struct during testing time.
An example of the expensive package would be:
package expensive
import "fmt"
type myStruct struct{}
func (m *myStruct) DoSomething() {
fmt.Println("In Do something")
}
func (m *myStruct) DoSomethingElse() {
fmt.Println("In do something else")
}
// CreateInstance is expensive to call
func CreateInstance() *myStruct {
return &myStruct{}
}
My main package that uses this then looks like this:
package main
import "play/expensive"
func main() {
thing := structToConstruct{expensive.CreateInstance}
thing.performAction()
}
type myInterface interface {
DoSomething()
}
type structToConstruct struct {
factoryFunction func() myInterface
}
func (s *structToConstruct) performAction() {
instance := s.factoryFunction()
instance.DoSomething()
}
However, this code complains with the error:
> .\main.go:6: cannot use expensive.CreateInstance (type func() *expensive.myStruct) as type func() myInterface in field value
However, *expensive.myStruct does implement the myInterface interface so I do not understand why Go is complaining about the type safety of this setup.
I've since realised after @jmaloney guideance that I could just wrapper my function like this in my main method:
wrapper := func() myInterface {
return expensive.CreateInstance()
}
thing := structToConstruct{wrapper}
and this then works but I still don't really understand why I can't use a struct that implements an interface when a function is expecting an instance of that interface to be returned especially when no type assertion/conversion is necessary on this fix as it is simply just calling the underlying factory function.
EDIT: I've since come across this proposal to add this to the language. The proposal was rejected:
答案1
得分: 6
getInstance
需要返回 myInterface
package main
import "fmt"
func main() {
var function func() myInterface
function = getInstance
newSomething := function()
newSomething.doSomething()
}
type myInterface interface {
doSomething()
}
type myStruct struct{}
func (m *myStruct) doSomething() {
fmt.Println("doing something")
}
func getInstance() myInterface {
return &myStruct{}
}
然而,*expensive.myStruct 确实实现了 myInterface 接口,所以我不明白为什么 Go 在这种设置中会抱怨类型安全性。
在这种情况下,你处理的不是 Go 的接口,而是你的结构体的类型签名。
当你首次声明结构体时,使用 factoryFunction func() *myFunction
,factoryFunction 现在必须始终与声明的签名匹配。
英文:
getInstance
needs to return myInterface
package main
import "fmt"
func main() {
var function func() myInterface
function = getInstance
newSomething := function()
newSomething.doSomething()
}
type myInterface interface {
doSomething()
}
type myStruct struct{}
func (m *myStruct) doSomething() {
fmt.Println("doing something")
}
func getInstance() myInterface {
return &myStruct{}
}
> However, *expensive.myStruct does implement the myInterface interface so I do not understand why Go is complaining about the type safety of this setup.
In that instance you were not dealing with Go's interfaces you were dealing with the type signature of your struct.
when you first declared your struct with factoryFunction func() *myFunction
factoryFunction now always needs to match the declared signature.
答案2
得分: 0
看看这个是否有帮助,有一个函数可以根据结构体返回一个接口。
package main
import "fmt"
func main() {
var instance myInterface
// getInstance 函数的返回值可以赋给类型为 myInterface 的变量
instance = getInstance()
var function func(*myStruct) myInterface
// 但是函数本身不能赋给返回 myInterface 的函数类型的变量。你会得到以下错误:
// cannot use getInstance (type func() *myStruct) as type func() myInterface in assignment
function = getMyInstance
r := function(instance.(*myStruct))
r.doSomething()
}
type myInterface interface {
doSomething()
}
type myStruct struct{}
func (m *myStruct) doSomething() {
fmt.Println("Done something")
}
func getInstance() *myStruct {
return &myStruct{}
}
func getMyInstance(myInst *myStruct) myInterface {
return myInst
}
可在 https://play.golang.org/p/h8iJ0i-Xym 查看完整代码。
英文:
See if this might help, have a function which returns an interface given the struct.
package main
import "fmt"
func main() {
var instance myInterface
// The return value of the getInstance function can be assigned to a variable
// of type myInterface
instance = getInstance()
var function func(*myStruct) myInterface
// But the function itself can't be assigned to a variable for a function
// that returns a myInterface. You get:
// cannot use getInstance (type func() *myStruct) as type func() myInterface in assignment
function = getMyInstance
r := function(instance.(*myStruct))
r.doSomething()
}
type myInterface interface {
doSomething()
}
type myStruct struct{}
func (m *myStruct) doSomething() {
fmt.Println("Done something")
}
func getInstance() *myStruct {
return &myStruct{}
}
func getMyInstance(myInst *myStruct) myInterface {
return myInst
}
Available at https://play.golang.org/p/h8iJ0i-Xym
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论