Interfaces Basics - Go
1. Type Admin
isn’t *Adimin
#
I found an interesting question on stackoverflow, this is the code:
type User interface {
Name() string
SetName(name string)
}
type Admin struct {
name string
}
func (a *Admin) Name() string {
return a.name
}
func (a *Admin) SetName(name string) {
a.name = name
}
OP tries to copy user1
’s value below:
func main() {
var user1 User
user1 = &Admin{name:"user1"}
fmt.Printf("User1's name: %s\n", user1.Name())
var user2 User
user2 = user1
user2.SetName("user2")
fmt.Printf("User2's name: %s\n", user2.Name()) // print: "user2"
fmt.Printf("User1's name: %s\n", user1.Name()) // print: "user2" too, How to make the user1 name does not change?
}
Everything pass by value in Golang, even assignment will make a copy. Therefore, I tried something like this:
var user1 User
user1 = &Admin{name:"user1"}
fmt.Printf("User1's name: %s\n", user1.Name())
var user2 User
user2 = *user1 // got an error: Invalid indirect of 'user1' (type 'User')
The erorr says that user’s type is User
not *User
, so cannot dereference it. Why user1 = &Admin{name:"user1"}
works fine?
An error occurred again below:
var user1 User
user1 = Admin{name:"user1"}
// error: Cannot use 'Admin{name:"user1"}' (type Admin) as the type User Type does not implement 'User' as the 'Name' method has a pointer receiver.
This is confusing, it says type Admin
doesn’t implement interface User
. You may wonder, you lied, Admin
has implemented all methods of User
, but wait, really?
In fact, it’s not type Admin
implementing interface User
but type *Admin
. Checking the definition of Name()
and SetName()
above, both of these two method have a pointer receiver, not a value receiver. This means the Name()
and GetName()
method are in the method set of the *Admin
type, but not in that of Admin
.
2. Any types can implement an interface#
“Any types can implement an interface.” is not accurate.
Any type can have its method set, and if there are all the methods of an interfacce in that type’s method set, we say this type implements this interface. This type can be a struct, function, map, array even an integer.
2.1. Array#
Get multiple parameters from cli with flag
package:
func Var(value Value, name string, usage string)
According to the docs, custom a type userFlag
to implement interface flag.Value
:
type usersFlag []user
func (u *usersFlag) String() string {
return fmt.Sprintf("%v", *u)
}
func (u *usersFlag) Set(value string) error {
values := strings.Split(value, ":")
if len(values) != 3 {
return fmt.Errorf("wrong format of auth, format: path:username:password")
}
*u = append(
*u,
user{
username: values[1],
password: values[2],
path: formatPath(values[0]),
},
)
return nil
}
Then you can use as follows:
type Param struct {
users []user
}
func (p *Param) init() {
var users usersFlag
flag.Var(&users, "auth", fmt.Sprintf("-auth <path:username:password>\n"+
"specify user for HTTP Basic Auth"))
flag.Parse()
p.users = users
}
2.2. function#
There is another function called http.Handle()
can be used to set routing info and callback, which has a different parameter compared with function http.HandleFunc()
:
func Handle(pattern string, handler Handler)
The second pamrameter is a Handler
which is an interface type defined in http package:
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
Function type customHandler
implements interface http.Handler
:
type customHandler func(http.ResponseWriter, *http.Request)
func (mwh customHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
http.Error(w, "method not supported", http.StatusMethodNotAllowed)
return
}
_, _ = fmt.Fprintf(w, "do something before custom handler mwh(w, r)")
mwh(w, r)
}
func main() {
callback := customHandler(func(w http.ResponseWriter, r *http.Request) {
_, _ = fmt.Fprintf(w, "hello there")
})
// customHandler implemented interface http.Handler, so its object can passed here
http.Handle("/hello", callback)
log.Fatal(http.ListenAndServe(":8080", nil))
}
NOTE: for struct, when you gengerate a value (“object”) you should use curly brcket {}
as below:
type Cat struct {
name string
age int
}
cat := Cat{name: "Kitten", age: 3}
for a function type, we use parentheses to produce a value of that type:
type myWebHandler func(http.ResponseWriter, *http.Request)
callback := myWebHandler(func(w http.ResponseWriter, r *http.Request) {
_, _ = fmt.Fprintf(w, "hello there")
})
3. Essence of interface variables#
An interface is conceptually a struct
with two fields. If we were to describe an interface in Go, it would look something like this.
type interface struct {
Type uintptr // points to the type of the interface implementation
Data uintptr // holds the data for the interface's receiver
}
Type
points to a structure that describes the type of the value that implements this interface. Data
points to the value of the implementation itself. The contents of Data
are passed as the receiver of any method called via the interface.
The Go memory model says that writes to a single machine word will be atomic, but interfaces are two word values. It is possible that another goroutine may observe the contents of the interface value while it is being changed.
Learn more: https://dave.cheney.net/2014/06/27/ice-cream-makers-and-data-races