无法将具有相同底层字段的结构体转换为不同类型。

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

Cannot convert struct to different type with same underlying fields

问题

根据这个问题和它的答案,我觉得我应该能够做以下操作:

req := &Req{}
req = TestReq(req)

但是在 VsCode 中我得到了这个错误:

无法将 req(类型为 *Req 的变量)转换为 TestReq 编译器(InvalidConversion)

这两个结构体不是有相同的底层字段吗?如果是这样,为什么第一个不能转换为第二个?

英文:
type Req struct {
	apiVersion       string
	path             string
	resourceEndpoint string
	accessKey        string
	log              *logrus.Entry
	incomingReq      interface{}
	httpClient       lib.HTTPClient
	redisClient      redis.Cmdable
	ctx				 context.Context
} 

type TestReq struct {
    Req
}

According to this this question and its answers, I feel like I should be able to do the following:

req := &Req{}
req = TestReq(req)

But I get this error in VsCode:
> cannot convert req (variable of type *Req) to TestReq compiler(InvalidConversion)

Don't these two structs have the same underlying fields? If so, why can't the first be converted into the second?

答案1

得分: 7

这两个结构体的底层字段不相同。Req有几个字段,TestReq只有一个类型为Req的字段,因此它们不能相互转换。嵌入并不会将嵌入类型的字段“复制”到嵌入者类型中。嵌入会添加一个单一的字段,可以通过其类型的未限定类型名称来引用。

嵌入的作用不是自动“复制”字段,而是将它们“提升”,同时也提升了嵌入类型的方法。

如果你有一个类型为TestReq的值,你可以使用未限定类型名称Req来引用嵌入字段,所以你可以这样做:

var tr TestReq
var r Req

r = tr.Req // 有效

tr.Req = r // 也是有效的

上述操作(语句)是赋值操作,因此它们会复制整个Req结构体的值。如果你想避免这种情况,可以嵌入一个指针,例如:

type TestReq struct {
    *Req
}

然后以下赋值操作只会复制一个指针值

```go
var tr = &TestReq{Req: &Req{}}
var r *Req

r = tr.Req // 有效

tr.Req = r // 也是有效的

(注意:`tr`本身可以是指针,也可以不是指针,这并不重要。)

<details>
<summary>英文:</summary>

&gt; Don&#39;t these two structs have the same underlying fields?

No, they don&#39;t. `Req` has several fields, `TestReq` has a single field of type `Req` so they are not [convertible](https://golang.org/ref/spec#Conversions) into each other. Embedding does not &quot;copy&quot; the fields of the embedded type to the embedder type. Embedding adds a single field which can be referred to by the unqualified type name of its type.

&lt;sup&gt;The use of embedding is not to automatically &quot;copy&quot; the fiels, but rather to have them &quot;promoted&quot;, also promoting the methods of the embedded type.&lt;/sup&gt;

If you have a value of type `TestReq`, you may use the unqualified type name `Req` to refer to the embedded field, so you may do something like this:

    var tr TestReq
    var r Req

    r = tr.Req // Valid

    tr.Req = r // This is also valid

The above operations (statements) are [assignments](https://golang.org/ref/spec#Assignments)  and as such, they copy the whole `Req` struct value. If you want to avoid that, you may embed a pointer, for example:

    type TestReq struct {
    	*Req
    }

And then the following assignments will only copy a pointer value:

	var tr = &amp;TestReq{Req: &amp;Req{}}
	var r *Req

	r = tr.Req // Valid

	tr.Req = r // This is also valid

(Note: `tr` itself may or may not be a pointer here, it doesn&#39;t matter.)

</details>



# 答案2
**得分**: 1

根据icza的建议,使用类型名称req为嵌入字段赋值。以下是相同功能的简单代码,为了简化,我将redis、logrus、context和http类型转换为interface{}。

```go
package main

import (
	"fmt"
)

type Req struct {
	apiVersion       string
	path             string
	resourceEndpoint string
	accessKey        string
	log              interface{}
	incomingReq      interface{}
	httpClient       interface{}
	redisClient      interface{}
	ctx              interface{}
}

type TestReq struct {
	Req
}

func main() {

	req1 := Req{"api01", "c:/doc/folder", "endkey", "ackey", "logs", [2]float64{2.0, 7.88}, "http", "redis", "ctx"}
	fmt.Println("ReqObject", req1)

	var testReq TestReq
	testReq.Req = req1
	fmt.Println("TestReqObject", testReq)
}

输出结果:

ReqObject {api01 c:/doc/folder endkey ackey logs [2 7.88] http redis ctx}
TestReqObject {{api01 c:/doc/folder endkey ackey logs [2 7.88] http redis ctx}}
英文:

Based on the suggestion icza, using the type name req to assign value to embedded field.Here is a simple code for the same , for simplicity I converted redis,logrus,context and http types as interface{}

package main
import (
&quot;fmt&quot;
)
type Req struct {
apiVersion       string
path             string
resourceEndpoint string
accessKey        string
log              interface{}
incomingReq      interface{}
httpClient       interface{}
redisClient      interface{}
ctx              interface{}
}
type TestReq struct {
Req
}
func main() {
req1 := Req{&quot;api01&quot;, &quot;c:/doc/folder&quot;, &quot;endkey&quot;, &quot;ackey&quot;, &quot;logs&quot;, [2]float64{2.0, 7.88}, &quot;http&quot;, &quot;redis&quot;, &quot;ctx&quot;}
fmt.Println(&quot;ReqObject&quot;,req1)
var testReq TestReq
testReq.Req = req1
fmt.Println(&quot;TestReqObject&quot;,testReq)
}

Output:

ReqObject {api01 c:/doc/folder endkey ackey logs [2 7.88] http redis ctx}
TestReqObject {{api01 c:/doc/folder endkey ackey logs [2 7.88] http redis ctx}}

huangapple
  • 本文由 发表于 2021年7月19日 21:20:55
  • 转载请务必保留本文链接:https://go.coder-hub.com/68441206.html
匿名

发表评论

匿名网友

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

确定