#Closing a wrapped Reader

20 messages · Page 1 of 1 (latest)

reef geyser
#

So I send net/http requests and i get res.Body that I have to Close. The problem is that I wrap that reader depending on the encoding:

    var reader io.Reader
    switch res.Header.Get("Content-Encoding") {
    case "br":
        reader = brotli.NewReader(res.Body)
    default:
        reader = res.Body
    }```
and then I subsequently return that wrapped reader. The problem is then how do i Close the body after I use it? Since its wrapped I don't have access to the body so I just can't close it.
#

I guess i can wrap it and have the Wrapped next to the body

#

if anyone has any better ideas

languid geyser
#

Oh, why not maintain a io.ReadCloser? and then just invoke reader.Close()?

reef geyser
cold jackal
#

what I believe csgeek was suggesting (and what I would recommend regardless) is for you to create your own type

#
var reader io.ReadCloser
switch {
  reader = struct{
    io.Closer
    io.Reader
  }{res.Body, brotli.NewReader(res.Body)}
case:
  reader = res.Body
}``` something like this should suffice
reef geyser
#

Thanks!

reef geyser
cold jackal
#

I don't understand what you mean by "it gets named by the last thing"
what's happening here is nothing clever, the fact the type is anonymous is also inconsequential beyond it being convenient

this is simply leveraging embedding
when you embed a type, the embedded methods may be promoted such that x.M() is valid, so long as there are no conflicting definitions
these promoted methods also participate in interface satisfaction
any type can be embedded, including interfaces

so we embed two of them, io.Reader and io.Closer
both are single method interfaces, these methods do not conflict
given that, our type automatically satisfies io.ReadCloser because it has a promoted read and close method

#

beyond interface satisfaction, embedding is just sugar

type Embedee struct {}
func(Embedee) Method() {}
type S struct {
  Embedee
}```

for this definition, all of these are valid
```go
var v S
_ = v.Embedee
v.Embedee.Method()
v.Method()```

note that `Embedee` is a field, you are just allowed to omit it
this affordance is also present when it comes to interface satisfaction
so, what happens when we call `Read` on our anonymous type?
simply `v.Reader.Read` just as if we did `v.Read`
#

I don't know how the compiler models it internally, but you can conceptualize as the compiler adding a "proxy method" for you go func (v myType) Read(d []byte) (int, error) { return v.Reader.Read(d) }

#

or, for our simpler example go func (s S) Method() { s.Embedee.Method() }

reef geyser
#

Ohhh now I get it

#

thank you so much

reef geyser
#

then i would have to wrap it manually right?

reef geyser
#
type bodyCloser struct {
    src io.ReadCloser
    dec io.ReadCloser
}

func (bc *bodyCloser) Read(p []byte) (n int, err error) {
    return bc.dec.Read(p)
}

func (bc *bodyCloser) Close() error {
    err1 := bc.dec.Close()
    err2 := bc.src.Close()
    return errors.Join(err1, err2)
}
``` is this okay?
cold jackal
#

at that point you may benefit from using a concrete type for the decoder, but yes

#

also fyi, if the thread is solved pings don't work, nor do new messages appear as unread