-1
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?

icza
  • 389,944
  • 63
  • 907
  • 827
papiro
  • 2,158
  • 1
  • 20
  • 29

2 Answers2

7

Don't these two structs have the same underlying fields?

No, they don't. Req has several fields, TestReq has a single field of type Req so they are not convertible into each other. Embedding does not "copy" 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.

The use of embedding is not to automatically "copy" the fiels, but rather to have them "promoted", also promoting the methods of the embedded type.

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 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 = &TestReq{Req: &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't matter.)

icza
  • 389,944
  • 63
  • 907
  • 827
  • One thing I don't understand is why `r` in your answer differs from `r := &Req{}`. Both structs are zeroed out but one is printable and one is not. – papiro Jul 19 '21 at 13:42
  • 2
    In my answer `r` is of type `Req`, where `&Req{}` is a pointer, it's of type `*Req`. What do you mean by "printable"? – icza Jul 19 '21 at 13:51
  • doh! Clearly I am a noob. Really appreciate your guidance on this! By printable I meant that `println` was failing. Please disregard :P – papiro Jul 19 '21 at 16:09
  • I think what's confusing to me is that the "promoting" isn't evident during definition of `tr`. One must know that the parent uses an embedded type during assignment, but afterwards the fields of the embedded struct do appear as fields of the parent and that relationship is not evident. – papiro Jul 20 '21 at 16:39
1

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 (
    "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)
}

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}}
Gopher
  • 721
  • 3
  • 8