为什么我不能将一个嵌套的结构赋值给父结构体在Go语言中?

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

Why can't I assign a embedded struct to a parent struct in go?

问题

我有以下代码,试图将嵌入结构分配给其父结构。有两组结构:Guider是父结构,DataBlock从它继承。方法func Put(x Guider)接受一个类型为Guider的参数。当我传递一个DataBlock变量时,它可以正常工作。

然而,另一种情况是Mockzerolog.Event继承,但在方法Test(e zerolog.Event)中无法传递参数。

我得到以下错误:

cannot use m (variable of type Mock) as type zerolog.Event in argument to Test

为什么这两种情况表现不同?我该如何使它们都正常工作?

package main

import (
	"fmt"

	"github.com/rs/zerolog"
)

type Guider interface {
	Guid() string
}
type FSEntity struct {
	guid string
}

func (e FSEntity) Guid() string {
	return e.guid
}

func Put(x Guider) {
	fmt.Printf("%+v\n", x)

}

type Mock struct {
	zerolog.Event
}

func Test(e zerolog.Event) {

}

//Child struct:

type DataBlock struct {
	FSEntity
	data []byte
}

func main() {
	myVar := DataBlock{}
	myVar.guid = "test"
	myVar.data = []byte("moar test")
	Put(myVar) // it works

	m := Mock{}
	Test(m) // it doesn't work. cannot use m (variable of type Mock) as type zerolog.Event in argument to Test
}
英文:

I have below code try to assign embed struct to its parent struct. There are two set of structure: Guider is the parent struct, DataBlock extends from it. The method func Put(x Guider) accept a parameter with type Guider. It works when I pass a DataBlock variable.

However, the other case is Mock extends from zerolog.Event, but it fails to pass the parameter on the method Test(e zerolog.Event)

I got the following error:

> cannot use m (variable of type Mock) as type zerolog.Event in argument to Test

Why are these two cases works differently? How can I make them both work?

package main

import (
	"fmt"

	"github.com/rs/zerolog"
)

type Guider interface {
	Guid() string
}
type FSEntity struct {
	guid string
}

func (e FSEntity) Guid() string {
	return e.guid
}

func Put(x Guider) {
	fmt.Printf("%+v\n", x)

}

type Mock struct {
	zerolog.Event
}

func Test(e zerolog.Event) {

}

//Child struct:

type DataBlock struct {
	FSEntity
	data []byte
}

func main() {
	myVar := DataBlock{}
	myVar.guid = "test"
	myVar.data = []byte("moar test")
	Put(myVar) // it works

	m := Mock{}
	Test(m) // it doesn't work. cannot use m (variable of type Mock) as type zerolog.Event in argument to Test
}

答案1

得分: 3

首先,几个定义:

多态性

多态性是指为不同类型的实体提供单一接口或使用单一符号表示多个不同类型的能力。

子类型化

子类型化(也称为子类型多态性或包含多态性)是一种类型多态性形式,其中子类型是与另一个类型(超类型)相关联的数据类型,通过某种可替代性概念,意味着通常编写用于操作超类型元素的程序元素(通常是子例程或函数)也可以操作子类型的元素。

继承

在面向对象编程中,继承是基于另一个对象(基于原型的继承)或类(基于类的继承)构建对象或类的机制,保留相似的实现。

对象组合

对象组合和对象聚合是将对象或数据类型组合成更复杂对象的密切相关的方式。


Golang 遵循组合优于继承的原则,例如它不支持继承。所以当你说

Mock extends from zerolog.Event

实际上是指 Mock 包含 zerolog.Event 结构体。

Golang 实现多态性的方式是使用接口。所有实现某个接口的类型都可以在其位置上使用。这就是你在使用 Guider 时看到的情况。

然而,对于简单的结构体来说,这种方式不起作用。zerolog.EventMock 内部的一个结构体。

因此,通常情况下,Test 函数应该接受某个接口作为参数,而且模拟事件和真实事件都应该实现这个接口。然而,看起来 zerolog 并没有为 Event 提供接口。所以你应该访问你结构体的 Event 字段。示例

英文:

First, a couple of definitions:

Polymorphism

Polymorphism is the provision of a single interface to entities of different types or the use of a single symbol to represent multiple different types.

Subtyping

Subtyping (also subtype polymorphism or inclusion polymorphism) is a form of type polymorphism in which a subtype is a datatype that is related to another datatype (the supertype) by some notion of substitutability, meaning that program elements, typically subroutines or functions, written to operate on elements of the supertype can also operate on elements of the subtype

Inheritance

In object-oriented programming, inheritance is the mechanism of basing an object or class upon another object (prototype-based inheritance) or class (class-based inheritance), retaining similar implementation.

Object composition

Object composition and object aggregation are closely related ways to combine objects or data types into more complex ones.


Golang follows composition over inheritance principle, e.g. it doesn't support inheritance. So when you're saying

> Mock extends from zerolog.Event

you actually mean that Mock includes zerolog.Event struct.

The way Golang implements polymorphism is interface. All types that implement some interface can be used in its place. It's what you see when use Guider.

However, it doesn't work for simple structs. zerolog.Event is a struct inside Mock.

So, normally, Test function should accept some interface as a parameter, and both mock and real event should implement this interface. However, it looks like zerolog doesn't provide interface for Event. So instead you should access the Event field of you struct. Example

答案2

得分: 2

Put(myVar)是合法的,因为myVar是一个包含(而不是继承或实现)一个实现了Guider接口的FSEntityDataBlock

由于Put接受一个Guider,所以对myVar的引用是兼容的,这是因为它包含的匿名FSEntity字段实现了GuiderFSEntity上的Guider实现(实际上)被提升到包含的结构体上(提供了一种委托接口的方式)。这仅在包含的字段是匿名的情况下发生。

但是在Test(m)的情况下,该函数接受一个zerolog.Event,它是一个struct类型,而不是一个接口。因此,没有"委托"的可能性。Test()必须传递一个zerolog.Event,在这种情况下,这要求您使用匿名字段的类型名称:

Type(m.Event)

一些额外的信息:

如果DataBlock包含了两个都实现了Guider的匿名字段,那么隐式的委托/提升就无法发生;Golang不知道应该委托/提升哪个包含的实现(如果有的话)。在这种情况下,您必须再次使用您希望传递给Put()函数的字段名称:

   // 假设... 
   type Foo string

   func (f Foo) Guid() string {
      return string(f)
   }

   // 并且...
   type DataBlock struct {
      FSEntity
      Foo
      data []byte
   }

   // 那么...
   Put(myVar)   // 现在是非法的

   // 必须使用以下方式之一:
   Put(myVar.FSEntity)
   Put(myVar.Foo)

无论是隐式还是显式,关键的区别是传递给Put()的是DataBlockmyVar)的一个字段,而不是myVar本身。

如果您想要将DataBlock作为一个实现了Guider接口的Guider接口传递给Put(),那么DataBlock本身必须实现Guider接口。

英文:

Put(myVar) is legal because myVar is a DataBlock which contains (not inherits from and not implements) an FSEntity which in turn implements the Guider interface.

Since Put accepts a Guider, the reference to myVar is compatible, by virtue of the anonymous FSEntity field it contains which implements Guider. The implementation of Guider on FSEntity is (in effect) elevated to the containing struct (providing a means of delegating interfaces). This only occurs if the contained field is anonymous.

But in the case of Test(m), the function accepts a zerolog.Event which is a struct type, not an interface. As such, there is no "delegation" possible. Test() must be passed a zerolog.Event and in this scenario, this requires that you use the type name of the anonymous field:

Type(m.Event)

Some bonus info:

If DataBlock contained two anonymous fields which both implemented Guider then implicit delegation/elevation cannot take place; golang does not know which of the contained implementations should be delegated to/elevated (if any). In that scenario you must again use the name of the field that you wish to pass to the Put() function:

   // given... 
   type Foo string

   func (f Foo) Guid() string {
      return string(f)
   }

   // and...
   type DataBlock struct {
      FSEntity
      Foo
      data []byte
   }

   // then...
   Put(myVar)   // is now illegal

   // and must instead use either/or:
   Put(myVar.FSEntity)
   Put(myVar.Foo)

Whether implicit or explicit, the crucial distinction is that it is a field of the DataBlock (myVar) that is passed to Put(), not myVar itself.

If you want to pass the DataBlock to Put(), using a Guider interface, then DataBlock must itself implement the Guider interface.

答案3

得分: 0

以谨慎的态度看待这个问题,因为我对zerolog包不熟悉。

你的Guider是一个接口,只要满足Guid()方法,它可以有任何底层类型。我猜测这是通过包含FSEntityDataBlock来实现的,FSEntity本身实现了Guid()方法,因此可能满足接口。

另一方面,我不知道应该实现哪些方法来满足zerolog.Event,或者它是否是一个接口,或者直接是一个结构体。如果它是一个接口,你可能需要实现它所需的方法,以便将DataBlock用作zerolog.Event类型。你可能需要深入研究这个方向,以获得一些非常具体的答案。

英文:

Take this with a grain of salt, since I'm not familiar with zerolog package.

Your Guider is an interface, which might have any underlying type as long as Guid() method is satisfied. I assume this is happening through DataBlock containing FSEntity, which itself implements Guid() method, therefore satisfies MIGHT the interface.

On the other hand, I don't know what methods should be implemented to satisfy zerolog.Event or if it's even an interface, or a struct straight up. If it's an interface, you might need to implement it's required methods to be able to use DataBlock as zerolog.Event type. You might want/need to dig into that direction for some very specific answers.

huangapple
  • 本文由 发表于 2023年1月23日 14:12:12
  • 转载请务必保留本文链接:https://go.coder-hub.com/75206147.html
匿名

发表评论

匿名网友

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

确定