0
package main

import (
    "fmt"
)

// -------- Library code. Can't change ------------
type Client struct {
    transport RoundTripper
}

type RoundTripper interface {
    Do()
}

type Transport struct{}

func (d Transport) Do() {}

var DefaultTransport RoundTripper = Transport{}

// -------- My code. Can change ------------
func changeTransport(r RoundTripper) {
    if r == nil {
        fmt.Println("transport is nil")
        r = DefaultTransport
    }
}

func main() {
    c := Client{}
    changeTransport(c.transport)
    fmt.Println(c.transport)
}

Output:

transport is nil
<nil>

Expected:

transport is nil
{}

Playground

I also tried this based on https://stackoverflow.com/a/44905592/6740589:

func changeTransport(r RoundTripper) {
    if r == nil {
        fmt.Println("transport is nil")
        d, ok := DefaultTransport.(Transport)
        if !ok {
            log.Fatal("impossible")
        }

        if t, ok := r.(*Transport); ok {
            t = &d
            fmt.Println("ignoreme", t)
        } else {
            log.Fatal("Uff")
        }

    }
}

Output:

transport is nil
2009/11/10 23:00:00 Uff

Playground

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
subtleseeker
  • 4,415
  • 5
  • 29
  • 41
  • 1
    one way is to change your method to accept a `*Client` and set its `transport` property. https://go.dev/play/p/3URxRJfrXHt – Sergio Tulentsev Apr 27 '23 at 10:52
  • 2
    A copy is made from anything that is passed, and you can only change the copy, not the original value. To change the original value, a pointer to it must be passed, and the function must modify the pointed value. If the interface value wraps a non-pointer, you can't change it. If it wraps a pointer, of course you can't change the original interface value, but you can "extract" the pointer and change the pointed value. – icza Apr 27 '23 at 10:53

1 Answers1

1

Use pointer of the RoundTripper interface as the changeTransport function argument to change pointer's value:

// -------- My code. Can change ------------
func changeTransport(r *RoundTripper) {
    if r != nil && *r == nil {
        fmt.Println("transport is nil")
        *r = DefaultTransport
    }
}

func main() {
    c := Client{}
    changeTransport(&c.transport)
    fmt.Println(c.transport)
}
transport is nil
{}

PLAYGROUND

kozmo
  • 4,024
  • 3
  • 30
  • 48
  • 1
    That's nice. Why &c.transport (address of a nil value) doesn't panic? – subtleseeker Apr 30 '23 at 10:10
  • `transport RoundTripper` initialize as `nil`-interface -> "iface" (https://go.dev/src/runtime/runtime2.go , https://go.dev/tour/methods/12) contains a pointer of the variable which implement the interface and `itab` (Interface table). `&c.transport` <- getting pointer of the `iface`. – kozmo May 01 '23 at 07:38