1

It's hard to explain, but how I can get a pointer of a something that implements some interface?


Consider the code below:

package main

import (
    "fmt"
    "unsafe"
)

type Interface interface {
    Example()
}

type StructThatImplementsInterface struct {
}

func (i *StructThatImplementsInterface) Example() {
}

type StructThatHasInterface struct {
    i Interface
}


func main() {
    sameInterface := &StructThatImplementsInterface{}

    struct1 := StructThatHasInterface{i: sameInterface}
    struct2 := StructThatHasInterface{i: sameInterface}

    TheProblemIsHere(&struct1)
    TheProblemIsHere(&struct2)

}

func TheProblemIsHere(s *StructThatHasInterface) {
    fmt.Printf("Pointer by Printf: %p \n", s.i)
    fmt.Printf("Pointer by Usafe: %v \n", unsafe.Pointer(&s.i))
}

https://play.golang.org/p/HoC5_BBeswA


The result will be:

Pointer by Printf: 0x40c138 
Pointer by Usafe: 0x40c140 
Pointer by Printf: 0x40c138 
Pointer by Usafe: 0x40c148 

Notice that the Printf gets the same value (because both StructThatHasInterface uses the same sameInterface). However, the unsafe.Pointer() returns distinct values.

How can I get the same result of Printf without use fmt, and reflect if possible?

Inkeliz
  • 892
  • 1
  • 13
  • 25
  • 1
    The address of the `i` fields in the two struct values have distinct values, therefore `unsafe.Pointer(&s.i)` has distinct values. . The reflect package is the way to go if you don't want to make assumptions on how interfaces are represented in memory (`reflect.ValueOf(s.i).Pointer()`). Why don't you want to use the reflect package? – Charlie Tumahai Dec 15 '19 at 01:19
  • @CeriseLimón I thought that exists an _easier_ way rather than reflect and understand how `reflect` get that value. I look at `reflect` and they use [`(*emptyInterface)(unsafe.Pointer(&i)).word`](https://github.com/golang/go/blob/7d30af8e17d62932f8a458ad96f483b9afec6171/src/reflect/value.go#L139-L152), but I can't make it works outside `reflect`, seems that have some _magic_ inside `reflect`. – Inkeliz Dec 15 '19 at 01:28
  • 2
    The expression `(*[2]uintptr)(unsafe.Pointer(&s.i))[1]`, works, but there's no guarantee that it will work in the future. The [Value.Pointer](https://godoc.org/reflect#Value.Pointer) method is the supported way to solve this problem. Other solutions require that you make assumptions about the memory layout. That does not seem easier to me. – Charlie Tumahai Dec 15 '19 at 01:42
  • @CeriseLimón could you explain why it works? Please, publish it as an answer, then I can mark as "correct answer". :) – Inkeliz Dec 15 '19 at 01:54

1 Answers1

4

In the current version of Go, an interface value is two words long. The concrete value or a pointer to the concrete value is stored in the second word. Use the following code to get the second word as a uintptr:

  u := (*[2]uintptr)(unsafe.Pointer(&s.i))[1]

This code is unsafe and is not guaranteed to work in future.

The supported way to get the pointer is:

  u := reflect.ValueOf(s.i).Pointer()
Charlie Tumahai
  • 113,709
  • 12
  • 249
  • 242