publicmiekg/gobook

Subversion checkout URL

You can clone with HTTPS or Subversion.

Fetching contributors…

Cannot retrieve contributors at this time

file 463 lines (423 sloc) 17.769 kb
 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 \epi{I have this phobia about having my body penetrated surgically. Youknow what I mean?}{\textit{eXistenZ}\\\textsc{TED PIKUL}}\noindent{}\gomarginpar{The following text is from \cite{go_interfaces}. Written by Ian Lance Taylor --- one of the authors of Go.}In Go, the word \first{\emph{interface}}{interface} is overloaded to mean several differentthings. Every type has an interface, which is the \emph{set of methodsdefined} for \index{interface!set of methods}that type. This bit of code defines a struct type \type{S} with one field, anddefines two methods for \type{S}.\begin{lstlisting}[caption=Defining a struct and methods on it,label=src:interface object]type S struct { i int }func (p *S) Get() int { return p.i }func (p *S) Put(v int) { p.i = v }\end{lstlisting}You can also define an \first{interface type}{interface!type}, which is simply a set of methods.This defines an interface \type{I} with two methods:\begin{lstlisting}type I interface {  Get() int  Put(int)}\end{lstlisting}\noindent\type{S} is a valid \emph{implementation} for interface \type{I}, because it defines the two methods which \type{I} requires. Note that this is true even though there is no explicit declaration that \type{S} implements \type{I}. A Go program can use this fact via yet another meaning of interface, which is an\first{interface value}{interface!value}:\begin{lstlisting}func f(p I) { |\longremark{Declare a function that takes an interface type %as the argument;}|    fmt.Println(p.Get()) |\longremark{As \var{p} implements interface \type{I} %it \emph{must} have the \func{Get()} method;}|    p.Put(1) |\longremark{Same holds for the \func{Put()} method.}|}\end{lstlisting}\showremarksHere the variable \var{p} holds a value of interface type. Because\type{S} implements \type{I}, we can call \func{f} passing in a pointer to a value of type \type{S}:\begin{lstlisting}var s S; f(&s)\end{lstlisting}The reason we need to take the address of \type{s}, rather than a value of type\type{S}, is because we defined the methods on \type{s} to operate onpointers, see the code above in listing \ref{src:interface object}.This is not a requirement --- we could have defined the methods to takevalues --- but then the \func{Put} method would not work as expected.The fact that you do not need to declare whether or not a type implements aninterface means that Go implements a form of \first{duck typing}{duck typing}\cite{duck_typing}. This is notpure duck typing, because when possible the Go compiler will staticallycheck whether the type implements the interface. However, Go does have apurely dynamic aspect, in that you can convert from one interface typeto another. In the general case, that conversion is checked at run time.If the conversion is invalid --- if the type of the value stored in theexisting interface value does not satisfy the interface to which it isbeing converted --- the program will fail with a run time error.Interfaces in Go are similar to ideas in several other programming languages:pure abstract virtual base classes in C++, typeclasses in Haskell or duck typingin Python. However there is no other language which combinesinterface values, static type checking, dynamic run time conversion, and norequirement for explicitly declaring that a type satisfies an interface. Theresult in Go is powerful, flexible, efficient, and easy to write.\subsection{Which is what?}Let's define another type that also implements the interface \type{I}:\begin{lstlisting}type R struct { i int }func (p *R) Get() int { return p.i }func (p *R) Put(v int) { p.i = v }\end{lstlisting}The function \func{f} can now accept variables of type \type{R} and \type{S}.Suppose you need to know the actual type in the function \func{f}. In Go you can figure that out by using a \first{type switch}{type switch}.\begin{lstlisting}func f(p I) {    switch t := p.(type) { |\longremark{The type switch. Use \key{(type)} in a \key{switch} %statement. We store the type in the variable \var{t};}|        case *S: |\longremark{The actual type of \var{p} is a pointer to \type{S};}|        case *R: |\longremark{The actual type of \var{p} is a pointer to \type{R};}|        case S: |\longremark{The actual type of \var{p} is a \type{S};}|        case R: |\longremark{The actual type of \var{p} is a \type{R};}|        default: |\longremark{It's another type that implements \type{I}.}|    }}\end{lstlisting}\showremarksUsing \key{(type)} outside a \key{switch} is illegal. A type switch isn't the only wayto discover the type at \emph{run-time}. You can also use a comma, ok'' form to see if an interface type implementsa specific interface:\begin{lstlisting}if t, ok := something.(I); ok {    // something implements the interface I   // t is the type it has} \end{lstlisting}When you are sure a variable implements an interface you can use:\begin{lstlisting}t := something.(I)\end{lstlisting}\subsection{Empty interface}Since every type satisfies the empty interface:\type{interface\{\}}. We can create a generic function which has an empty interface as its argument:\begin{lstlisting}[caption=A function with an empty interface argument,label=src:interface empty]func g(something interface{}) int {     return something.(I).Get() }\end{lstlisting}The \lstinline{return something.(I).Get()} is the tricky bit in this function.The value \var{something} has type \type{interface\{\}}, meaning no guaranteeof any methods at all: it could contain any type. The \lstinline{.(I)}is a \first{type assertion}{type assertion} which converts \var{something} to an interface oftype \type{I}. If we have that type we can invoke the \func{Get()}function.So if we create a new variable of the type \type{*S}, we can justcall \func{g()}, because \type{*S} also implements the empty interface.\begin{lstlisting}s = new(S)fmt.Println(g(s));\end{lstlisting}The call to \func{g} will work fine and will print 0. If we howeverinvoke \func{g()} with a value that does not implement \type{I} we havea problem:\begin{lstlisting}[caption=Failing to implement an interface,label=src:interface fail]i := 5 |\coderemark{Make i a lousy'' \texttt{int}}|fmt.Println(g(i))\end{lstlisting}This compiles, but when we run this we get slammed with:\noindent\error{panic: interface conversion: int is not main.I: missingmethod Get}\noindent{}Which is completely true, the built-in type \type{int} does nothave a \func{Get()} method.\section{Methods}Methods are functions that have a receiver (see chapter\ref{chap:functions}).You can define methods on any type (except on non-local types, this includesbuilt-in types: the type \type{int} can not have methods).You can however make a new integer type with its own methods. For example:\begin{lstlisting}type Foo intfunc (self Foo) Emit() {  fmt.Printf("%v", self)}type Emitter interface {  Emit()}\end{lstlisting}Doing this on non-local (types defined in other packages) types yields:% Empty line here is critical, otherwise no new paragraph is created\begin{minipage}{.5\textwidth}\begin{lstlisting}[linewidth=.7\textwidth,caption=Failure extending built-in types]func (i int) Emit() {  fmt.Printf("%d", i)}\end{lstlisting}\noindent\error{cannot define new methods\\ on non-local type int}\end{minipage}\begin{minipage}{.5\textwidth}\begin{lstlisting}[caption=Failure extending non-local types]func (a *net.AddrError) Emit() {  fmt.Printf("%v", a)}\end{lstlisting}\noindent\error{cannot define new methods\\ on non-local type net.AddrError}\end{minipage}\paragraph{} %% needed otherwise the minipage flows over\subsection{Methods on interface types}An interface defines a set of methods. A method contains the actual code.In other words, an interface is the definition and the methods are the implementation.So a receiver can not be an interfacetype, doing so results in a \error{invalid receiver type ...} compilererror. The authoritative word from the language spec \cite{go_spec}:\begin{quote}The receiver type must be of the form \type{T} or \type{*T} where\type{T} is a type name. \type{T} is called the receiver base type or just base type. The base type mustnot be a pointer or interface type and must be declared in the samepackage as the method.\end{quote}\begin{lbar}[Pointers to interfaces]Creating a pointer to an interface value is a useless action in Go.It is in fact illegal tocreate a pointer to an interface value. The release notes for the release \gorelease{2010-10-13}thatmade them illegal leave no room for doubt:\begin{quote}The language change is that uses of pointers to interface values no longer automatically de-reference the pointer. A pointer to an interface value is more often a beginner's bug than correct code.\end{quote}From the \cite{go_faq}. If not for this restriction, this code:\begin{lstlisting}var buf bytes.Bufferio.Copy(buf, os.Stdin)\end{lstlisting}would copy standard input into a copy of \var{buf}, not into \var{buf} itself. This is almost never the desired behavior.\end{lbar}\section{Interface names}By convention, one-method interfaces are named by the method name plusthe \emph{-er} suffix: Read\emph{er}, Writ\emph{er}, Formatt\emph{er} etc.There are a number of such names and it's productive to honor them andthe function names they capture. \func{Read}, \func{Write},\func{Close}, \func{Flush}, \func{String} andso on have canonical signatures and meanings. To avoid confusion, don'tgive your method one of those names unless it has the same signature andmeaning. Conversely, if your type implements a method with the samemeaning as a method on a well-known type, give it the same name andsignature; call your string-converter method \func{String} not\func{ToString}.\gomarginpar{Text copied from \cite{effective_go}.}\section{A sorting example}\label{sec:a sorting example}Recall the Bubblesort exercise (Q\ref{ex:bubble}), where we sorted anarray of integers:\begin{lstlisting}func bubblesort(n []int) {    for i := 0; i < len(n)-1; i++ { for j := i + 1; j < len(n); j++ { if n[j] < n[i] { n[i], n[j] = n[j], n[i] } }    }}\end{lstlisting}A version that sorts strings is identical except for the signature ofthe function:\begin{lstlisting}func bubblesortString(n []string) { /* ... */ }\end{lstlisting}Using this approach would lead to two functions, one for each type. By usinginterfaces we can make this more \index{generic} generic.Let's create a new function that will sort both strings andintegers, something along the lines of this non-working example:\begin{lstlisting}func sort(i []interface{}) { |\longremark{Our function will receive a slice of %empty interfaces;}|    switch i.(type) { |\longremark{Using a type switch we find out what the %actual type is of the input;}| case string: |\longremark{And then sort accordingly;}| // ... case int: // ...    }    return /* ... */ |\longremark{Return the sorted slice.}|}\end{lstlisting}\showremarksBut when we call this function with \lstinline|sort([]int{1, 4, 5})|, itfails with:\\\noindent\error{cannot use i (type []int) as type []interface { } in function argument}This is because Go can not easily convert to a \emph{slice} of interfaces.Just converting to an interface is easy, but to a slice is much more costly.To keep a \gomarginpar{The full mailing list discussion on this subjectcan be found at \cite{go_nuts_interfaces}.}long story short: Go does not (implicitly) convert slices for you.So what is the Go way of creating such a generic'' function? Instead of doing the type inference ourselves with a type switch, we letGo do it implicitly:The following steps are required:\begin{enumerate}\item Define an interface type (called \type{Sorter} here) with a number of methods needed for sorting.We will at least need a function to get the length of the slice,a function to compare two values and a swap function;\begin{lstlisting}type Sorter interface {    Len() int |\coderemark{\texttt{len()} as a method}|    Less(i, j int) bool |\coderemark{\texttt{p[j] $<$ p[i]} as a method}|    Swap(i, j int) |\coderemark{\texttt{p[i], p[j] = p[j], p[i]} as a method}|}\end{lstlisting}\item Define new types for the slices we want to sort. Note that wedeclare slice types;\begin{lstlisting}type Xi []inttype Xs []string\end{lstlisting}\item Implementation of the methods of the \type{Sorter} interface.For integers:\begin{lstlisting}func (p Xi) Len() int { return len(p) }func (p Xi) Less(i int, j int) bool { return p[j] < p[i] }func (p Xi) Swap(i int, j int) { p[i], p[j] = p[j], p[i] }\end{lstlisting}And for strings:\begin{lstlisting}func (p Xs) Len() int { return len(p) }func (p Xs) Less(i int, j int) bool { return p[j] < p[i] }func (p Xs) Swap(i int, j int) { p[i], p[j] = p[j], p[i] }\end{lstlisting}\item Write a \emph{generic} Sort function that works on the \type{Sorter} interface.\begin{lstlisting}func Sort(x Sorter) { |\longremark{\var{x} is now of the \texttt{Sorter} type;}|    for i := 0; i < x.Len() - 1; i++ { |\longremark{Using the defined functions, we implement Bubblesort.}| for j := i + 1; j < x.Len(); j++ { if x.Less(i, j) { x.Swap(i, j) } }    }}\end{lstlisting}\showremarks\end{enumerate}We can now use your generic \func{Sort} function as follows:\begin{lstlisting}ints := Xi{44, 67, 3, 17, 89, 10, 73, 9, 14, 8}strings := Xs{"nut", "ape", "elephant", "zoo", "go"}Sort(ints)fmt.Printf("%v\n", ints)Sort(strings)fmt.Printf("%v\n", strings)\end{lstlisting}\subsection{Listing interfaces in interfaces}Take a look at the following example of an interface definition, this one is from the package \package{container/heap}:\begin{lstlisting}type Interface interface {    sort.Interface    Push(x interface{})    Pop() interface{}}\end{lstlisting}Here another interface is listed inside the definition of \type{heap.Interface}, thismay look odd, but is perfectly valid, remember that on the surface an interface is nothingmore than a listing of methods. \type{sort.Interface} is also such a listing, so it isperfectly legal to include it in the interface.\subsection{Introspection and reflection}\label{sec:introspection and reflection}In the following example we want to look at the tag'' (here named namestr'') defined in thetype definition of \type{Person}. To do this we need the\package{reflect}\index{package!reflect} package (there is no other way in Go). Keep in mindthat looking at a tag means going back to the \emph{type} definition. Sowe use the \package{reflect} package to figure out the type of the variableand \emph{then} access the tag.\input{fig/reflection.tex}\showremarks%% look at layoutTo make the difference between types and values more clear,that a look at the following code:\begin{lstlisting}[caption=Reflection and the type and value]func show(i interface{}) {    switch t := i.(type) {      case *Person:        t := reflect.TypeOf(i) |\coderemark{Go for type meta data}|        v := reflect.ValueOf(i) |\coderemark{Go for the actual values}| tag := t.Elem().Field(0).Tag |\longremark{Here we want to get to the tag''. %So we need \func{Elem()} to redirect the pointer, access the first field and get the tag. %Note we operate on \var{t} a \type{reflect.Type};}| name := v.Elem().Field(0).String() |\longremark{Now we want to get access to the %\emph{value} of one of the members and we %employ\newline\lstinline{Elem()} on \var{v} to do the redirection. %Now we have arrived at the structure. Then we go the the first field %\lstinline{Field(0)} and invoke the \lstinline{String()} method on %it. %\begin{figure}[H] %\hskip3\baselineskip\parbox{0.7\textwidth}{\caption[Peeling away the layers using reflection]{Peeling away the %layers using reflection. %Going from a \type{*Person} via \mbox{\func{Elem()}} using the %methods described in \prog{go doc reflect} to get the %actual \type{string} contained within.}} %\label{fig:reflection} %\begin{center} %\includegraphics[scale=0.75]{fig/reflection.pdf} %\end{center}\end{figure} %}|    }}\end{lstlisting}\showremarksSetting a value works similarly as getting a value, but only works on\emph{exported} members. Again some code:\begin{minipage}{.5\textwidth}\begin{lstlisting}[caption=Reflect with private member]type Person struct { name string "namestr" |\coderemark{name}| age int}func Set(i interface{}) { switch i.(type) { case *Person:  r := reflect.ValueOf(i)  r.Elem(0).Field(0).SetString("Albert Einstein")  }}\end{lstlisting}\end{minipage}\hspace{2em}\begin{minipage}{.5\textwidth}\begin{lstlisting}[caption=Reflect with public member]type Person struct { Name string "namestr" |\coderemark{\emph{N}ame}| age int}func Set(i interface{}) { switch i.(type) { case *Person:  r := reflect.ValueOf(i)  r.Elem().Field(0).SetString("Albert Einstein")  }}\end{lstlisting}\end{minipage}The code on the left compiles and runs, but when you run it, you are greeted with astack trace and a \emph{run time} error:\noindent\error{panic: reflect.Value.SetString using value obtained using unexported field}\noindent{}The code on the right works OK and sets the member \var{Name}to Albert Einstein''. Of course this only works when you call \func{Set()}with a pointer argument.\section{Exercises}\input{ex-interfaces/ex-interfaces.tex}\input{ex-interfaces/ex-pointers-and-reflect.tex}\input{ex-interfaces/ex-minmax.tex}\cleardoublepage\section{Answers}\shipoutAnswer
Something went wrong with that request. Please try again.