Go函数类型返回结构体与接口的使用

huangapple go评论84阅读模式
英文:

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:

https://github.com/golang/go/issues/12754

答案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{}
}

Playground 示例

然而,*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{}
}

Playground example

> 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

huangapple
  • 本文由 发表于 2017年2月18日 01:07:10
  • 转载请务必保留本文链接:https://go.coder-hub.com/42303611.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定