2020年9月8日 星期二

[ 常見問題 ] “ is pointer to interface, not interface” confusion

 Source From Here

Question
I've got this problem which seems a bit weird to me. Take a look at this snippet of code:
  1. package main  
  2.   
  3. import (  
  4.     "fmt"  
  5. )  
  6.   
  7. type IterA interface{  
  8.     Filter(s *string) bool  
  9. }  
  10.   
  11. type S1 struct {  
  12.     key string  
  13.     val string  
  14. }  
  15.   
  16. func (so *S1) Filter(s *string) bool {  
  17.     fmt.Printf("Filter %#v\n", *so);  
  18.     return true;  
  19. }  
  20.   
  21. type IterB interface{  
  22.     FunA(f *IterA)  
  23.     FunB(i int)  
  24. }  
  25.   
  26. type S2 struct{  
  27.     id int  
  28. }  
  29.   
  30. func (so *S2) FunA(f *IterA) {  
  31.     fmt.Printf("FunA by %#v\n", *so)  
  32. }  
  33.   
  34. func (so *S2) FunB(i int) {  
  35.     fmt.Printf("FunB by %#v\n", *so)  
  36. }  
  37.   
  38.   
  39. func main(){  
  40.     fmt.Println("test");  
  41.     s1 := &S1{"key""value"};  
  42.     data := "test"  
  43.     s1.Filter(&data)  
  44.     p_s1 := s1  
  45.     s2 := S2{1}  
  46.     s2.FunA(p_s1)  
  47.     s2.FunB(0)  
  48. }  
when I compile the project:
# go build -o test faq_q1
# faq_q1
./main.go:46:12: cannot use p_s1 (type *S1) as type *IterA in argument to s2.FunA:

*IterA is pointer to interface, not interface

Why can't I pass the pointer p_s1 of S1 structure to function FunA(f *IterA) which will accept the argument as pointer of interface IterA?

HowTo
So you're confusing two concepts here. A pointer to a struct and a pointer to an interface are not the same. An interface can store either a struct directly or a pointer to a struct. In the latter case, you still just use the interface directly, not a pointer to the interface. For example:
  1. type Fooer interface {  
  2.     Dummy()  
  3. }  
  4.   
  5. type Foo struct{}  
  6.   
  7. func (f Foo) Dummy() {}  
  8.   
  9. func main() {  
  10.     var f1 Foo  
  11.     var f2 *Foo = &Foo{}  
  12.   
  13.     DoFoo(f1)  
  14.     DoFoo(f2)  
  15. }  
  16.   
  17. func DoFoo(f Fooer) {  
  18.     fmt.Printf("[%T] %+v\n", f, f)  
  19. }  
Output:
[main.Foo] {}
[*main.Foo] &{}

In both cases, the f variable in DoFoo is just an interface, not a pointer to an interface. However, when storing f2, the interface holds a pointer to a Foo structure.

Pointers to interfaces are almost never useful. In fact, the Go runtime was specifically changed a few versions back to no longer automatically dereference interface pointers (like it does for structure pointers), to discourage their use. In the overwhelming majority of cases, a pointer to an interface reflects a misunderstanding of how interfaces are supposed to work.

However, there is a limitation on interfaces. If you pass a structure directly into an interface, only value methods of that type (ie. func (f Foo) Dummy(), not func (f *Foo) Dummy()) can be used to fulfill the interface. This is because you're storing a copy of the original structure in the interface, so pointer methods would have unexpected effects (ie. unable to alter the original structure). Thus the default rule of thumb is to store pointers to structures in interfaces, unless there's a compelling reason not to.

Specifically with your code, if you change the FunA function signature to:
  1. func (so *S2) FunA(f IterA)  
Your code will work as expected. Here's a couple of good references for understanding how methods, types, and interfaces work and integrate with each other in Go:
Medium - Interfaces in Go
The Go Blog - The Laws of Reflection

沒有留言:

張貼留言

[Git 常見問題] error: The following untracked working tree files would be overwritten by merge

  Source From  Here 方案1: // x -----删除忽略文件已经对 git 来说不识别的文件 // d -----删除未被添加到 git 的路径中的文件 // f -----强制运行 #   git clean -d -fx 方案2: 今天在服务器上  gi...