Merge pull request #1 from astaxie/master

merged
This commit is contained in:
vz
2016-12-17 22:43:42 +08:00
committed by GitHub
44 changed files with 3404 additions and 203 deletions

View File

@@ -219,7 +219,7 @@ Gruppierter Ansatz.
prefix string
)
Wird innerhalb von `constant()` einer Konstanten das Schlüsselwort `iota` als Wert zugewiesen, hat sie den Wert `0`. Werden den folgenden Konstanten keinee expliziten Werte zugewiesen, wird der letzte zugeweise Wert von `iota` um 1 erhöht und und der folgenden Konstante zugewiesen. Dieses Verhalten beleuchten wir im folgenden Absatz.
Wird innerhalb von `constant()` einer Konstanten das Schlüsselwort `iota` als Wert zugewiesen, hat sie den Wert `0`. Werden den folgenden Konstanten keine expliziten Werte zugewiesen, wird der letzte zugeweise Wert von `iota` um 1 erhöht und der folgenden Konstante zugewiesen. Dieses Verhalten beleuchten wir im folgenden Absatz.
### Aufzählen mit iota

View File

@@ -333,7 +333,7 @@ Nun können wir den Wert von `x` in der Funktion ändern. Aber warum nutzen wir
### defer
Go besitzt mit `defer` ein weiteres nützliches Schlüsselwort. Du kannst `defer` mehrmals in einer Funktion nutzen. Sie werden in umgekehrter Reihenfolge am Ende einer Funktion ausgeführt. Im dem Fall, dass Dein Programm eine Datei öffnet, muss diese erst wieder geschlossen werden, bevor Fehler zurückgeben werden können. Schauen wir uns ein paar Beispiele an.
Go besitzt mit `defer` ein weiteres nützliches Schlüsselwort. Du kannst `defer` mehrmals in einer Funktion nutzen. Sie werden in umgekehrter Reihenfolge am Ende einer Funktion ausgeführt. Im Fall, dass Dein Programm eine Datei öffnet, muss diese erst wieder geschlossen werden, bevor Fehler zurückgeben werden können. Schauen wir uns ein paar Beispiele an.
func LesenSchreiben() bool {
file.Open("Datei")
@@ -520,4 +520,4 @@ Es gibt spezielle Operatoren beim Importieren von Paketen, die Anfänger oftmals
- [Inhaltsverzeichnis](preface.md)
- Vorheriger Abschnitt: [Grundlagen von Go](02.2.md)
- Nächster Abschnitt: [Struct](02.4.md)
- Nächster Abschnitt: [Struct](02.4.md)

View File

@@ -179,7 +179,7 @@ In Go können alle Datenttypen eingebettet werden.
fmt.Println("Ihre bevorzugte Nummer lautet", jane.int)
}
Im oberen Beispiel ist erkenntlich, dass alle Datentypen eingebettet werden und Funktion auf ihre Werte zugreifen können.
Im oberen Beispiel ist erkenntlich, dass alle Datentypen eingebettet werden und Funktionen auf ihre Werte zugreifen können.
Aber es gibt noch ein kleines Problem. Was geschieht, wenn Mensch die Eigenschaft `telefon` besitzt und Student eine Eigenschaft mit dem gleichen Namen besitzt?
@@ -211,4 +211,4 @@ Go nutzt einen einfachen Weg zur Unterscheidung. Um die Eigenschaft `telefon` vo
- [Inhaltsverzeichnis](preface.md)
- Vorheriger Abschnitt: [Kontrollstrukturen und Funktionen](02.3.md)
- Nächster Abschnitt: [Objektorientiertes Programmieren](02.5.md)
- Nächster Abschnitt: [Objektorientiertes Programmieren](02.5.md)

View File

@@ -1,6 +1,6 @@
# 2.5 Objektorientierte Programmierung
In den letzen beiden Abschnitten hatten wir uns mit Funktionen und Structs beschäftigt, aber hast Du jemals daran gedacht, Funktionen als Eigenschaft in einem Struct zu verwenden? In diesem Abschnitt werden ich Dir eine besondere Art von Funktionen vorstellen, die einen Reciever (engl. to recieve - empfangen) besitzen. Sie werden auch `Methoden` genannt.
In den letzen beiden Abschnitten hatten wir uns mit Funktionen und Structs beschäftigt, aber hast Du jemals daran gedacht, Funktionen als Eigenschaft in einem Struct zu verwenden? In diesem Abschnitt werde ich Dir eine besondere Art von Funktionen vorstellen, die einen Reciever (engl. to recieve - empfangen) besitzen. Sie werden auch `Methoden` genannt.
## Methoden
@@ -306,4 +306,4 @@ Nun bist Du bereit, Dein eigenes, objektorientiers Programm zu schreiben. Auch M
- [Inhaltsverzeichnis](preface.md)
- Vorheriger Abschnitt: [Struct](02.4.md)
- Nächster Abschnitt: [Interface](02.6.md)
- Nächster Abschnitt: [Interface](02.6.md)

View File

@@ -89,7 +89,7 @@ Zudem implementiert jeder Datentyp das leere Interface `interface{}`, da es kein
### Interface als Datentyp
Welche Arten von Werten können mit einem Interface verknüpft werden? Wen wir eine Variable vom Typ Interface definieren, dann kann jeder Datentyp, der das Interface implementiert wird, der Variable zugewiesen werden.
Welche Arten von Werten können mit einem Interface verknüpft werden? Wen wir eine Variable vom Typ Interface definieren, dann kann jeder Datentyp, der das Interface implementiert, der Variable zugewiesen werden.
Es ist wie im oberen Beispiel. Erstellen wir eine Variable "m" mit dem Interface Männer, kann jeder Student, Mensch oder Mitarbeiter "m" zugewiesen werden. So könnten wir ein Slice mit dem Interface Männer jeden Datentyp hinzufügen, der ebenfalls das Interface Männer implementiert. Bedenke aber, dass sich das Verhalten von Slices ändert, wenn dies Elemente eines Interface statt eines Datentypes verwendet.

View File

@@ -23,10 +23,10 @@ In diesem Kapitel haben wir uns hauptsächlich mit den 25 Schlüsselwörtern in
- `map` definiert eine Map, welche Hashtabellen in anderen Programmiersprachen ähneln.
- `range` wird genutzt, um Daten aus einem `slice`, einer `map` oder einem`channel` zu erhalten.
Wenn Du verstanden was, wie die 25 Schlüsselwörter einzusetzen sind, dann hast Du bereits eine Menge über Go gelernt.
Wenn Du verstanden hast, wie die 25 Schlüsselwörter einzusetzen sind, dann hast Du bereits eine Menge über Go gelernt.
## Links
- [Inhaltsverzeichnis](preface.md)
- Vorheriger Abschnitt: [Nebenläufigkeit](02.7.md)
- Nächstes Kapitel: [Grundlagen des Internets](03.0.md)
- Nächstes Kapitel: [Grundlagen des Internets](03.0.md)

View File

@@ -16,5 +16,5 @@ En este capítulo, te mostraremos cómo instalar y configurar tu propio ambiente
## Links
- [Directorio](preface.md)
- [Índice](preface.md)
- Sección siguiente: [Instalación](01.1.md)

View File

@@ -28,21 +28,21 @@ En Windows, debes instalar MinGW antes para poder instalar gcc. No olvides confi
El equipo Go usa [Mercurial](http://mercurial.selenic.com/downloads/) para manejar su código fuente, por lo que debes instalar esta herramienta para poder bajar el código fuente de Go.
En este punto ejecuta los siguientes comandos para clonar el código fuente de Go y compilarlo. (Clonará el código fuente en tú directorio actual. Cambia tu ruta actual antes de continuar. Esto puede tomar algún tiempo.)
```
hg clone -u release https://code.google.com/p/go
cd go/src
./all.bash
```
Una instalación exitosa finalizará con el mensaje "ALL TESTS PASSED."
En Windows puedes lograr lo mismo ejecutando `all.bat`.
Si estas usando Windows el paquete de instalación establecerá tus variables de entorno automáticamente. En sistemas tipo Unix necesitas establecer estas variables de la siguiente manera. (Si tu versión de Go es mayor que 1.0 no debes establecer $GOBIN ya que estará relacionada automáticamente a tu $GOROOT/bin del que hablaremos en la sección siguiente)
```
export GOROOT=$HOME/go
export GOBIN=$GOROOT/bin
export PATH=$PATH:$GOROOT/bin
```
Si ves la siguiente información en tu pantalla, todo está listo.
![](images/1.1.mac.png?raw=true)
@@ -66,11 +66,11 @@ Si eres un usuario de Mac recomiendo fuertemente que bajes el paquete de 64-bits
Los usuarios de Linux pueden escribir `uname -a` en la terminal para ver la información del sistema.
Un sistema operativo de 64-bits mostrará lo siguiente:
```
<alguna descripción> x86_64 x86_64 x86_64 GNU/Linux
// algunas versiones como Ubuntu 10.04 mostrarán de la siguiente forma
x86_64 GNU/Linux
```
En cambio los sistemas operativos de 32-bits mostrarán:
<alguna descripción> i686 i686 i386 GNU/Linux
@@ -92,32 +92,32 @@ Ve a la [página de descarga](https://golang.org/dl/), escoge `go1.5.3.windows-3
### GVM
GVM es una herramienta de control de múltiples versiones de Go desarrollado por terceros, parecida a rvm para ruby. Es bien fácil de utilizar. Instala gvm escribiendo los siguientes comandos en tu terminal:
```
bash < <(curl -s https://raw.github.com/moovweb/gvm/master/binscripts/gvm-installer)
```
Luego instalamos Go usando los siguientes comandos:
```
gvm install go1.0.3
gvm use go1.0.3
```
Cuando el proceso ha finalizado estamos listos.
### apt-get
Ubuntu es la versión de escritorio más popular de Linux. Utiliza `apt-get` para manejar paquetes. Podemos instalar Go usando los siguientes comandos.
```
sudo add-apt-repository ppa:gophers/go
sudo apt-get update
sudo apt-get install golang-stable
```
### Homebrew
Homebrew es una herramienta para manejar software comúnmente usada en la Mac. Simplemente escribe lo siguiente para instalar Go.
```
brew install go
```
## Links
- [Directorio](preface.md)
- [Índice](preface.md)
- Sección anterior: [Configurando el entorno de Go](01.0.md)
- Sección siguiente: [$GOPATH y el ambiente de trabajo](01.2.md)

View File

@@ -5,9 +5,9 @@
Todos los comandos de Go dependen de una importante variable de entorno llamada $GOPATH. Favor notar que esta no es la variable $GOROOT que es la que muestra dónde Go está instalado. Esta variable apunta al ambiente de trabajo de Go en tu computadora (Yo uso esta ruta en mi computadora; si no tienes la misma estructura de directorios, por favor reemplaza por la que tengas).
En sistemas tipo Unix, la variable debe usar así:
```
export GOPATH=/home/apple/mygo
```
En Windows, necesitas crear una nueva variable de entorno llamada GOPATH, luego estable su valor a `c:\mygo` (Este valor depende dónde tu ambiente de trabajo esté ubicado)
Está bien tener más de una ruta de ambiente de trabajo en $GOPATH, pero recuerda que debes usar `:`(`;` en Windows) para separarlos. En este punto, `go get` guardará el contenido de tu primera ruta en $GOPATH.
@@ -22,17 +22,17 @@ En este libro, utilizo `mygo` cómo mi única ruta en $GOPATH.
## Directorio de paquetes
Crea archivos de código fuente de paquetes de la siguiente forma `$GOPATH/src/mymath/sqrt.go` (`mymath` es el nombre del paquete) ( ***El autor utiliza `mymath` cómo su nombre de paquete, el mismo nombre para el directorio que contiene los archivos de código fuente del paquete)
Crea archivos de código fuente de paquetes de la siguiente forma `$GOPATH/src/mymath/sqrt.go` (`mymath` es el nombre del paquete) ( **El autor utiliza `mymath` cómo su nombre de paquete, el mismo nombre para el directorio que contiene los archivos de código fuente del paquete**)
Cada vez que creas un paquete, deberías crear un nuevo folder en el directorio `src`. Los nombres de los directorios usualmente son los mismos que el paquete que vas a utilizar. Puedes tener directorios de múltiples niveles si lo deseas. Por ejemplo, si creas el directorio `$GOPATH/src/github.com/astaxie/beedb`, entonces la ruta del paquete sería `github.com/astaxie/beedb`. El nombre del paquete será el último directorio en tu ruta, que es `beedb` en este caso.
Ejecuta los siguientes comandos. (Ahora el autor mostrará unos ejemplos)
```
cd $GOPATH/src
mkdir mymath
```
Crea un nuevo archivo llamado `sqrt.go`, escribe el siguiente contenido en el archivo.
```
// Código fuente de $GOPATH/src/mymath/sqrt.go
package mymath
@@ -43,7 +43,7 @@ Crea un nuevo archivo llamado `sqrt.go`, escribe el siguiente contenido en el ar
}
return z
}
```
Ahora el directorio de mi paquete ha sido creado y su código ha sido escrito. Recomiendo que uses el mismo nombre para tus paquetes y sus directorios correspondientes y que los directorios contenga todo el código fuente del paquete.
## Compilar paquetes
@@ -54,24 +54,24 @@ Ya hemos creado nuestra paquete, pero cómo lo compilamos para propósitos prác
2. Ejecuta el comando de arriba, con la diferencia de que suministras el nombre del archivo como parámetro `go install mymath`.
Después de compilar podemos abrir el siguiente directorio.
```
cd $GOPATH/pkg/${GOOS}_${GOARCH}
// puedes ver que el archivo fue generado
mymath.a
```
Este archivo cuyo sufijo es `.a`, es el archivo binario de nuestro paquete. Cómo lo usamos?
Obviamente, necesitamos crear una nueva aplicación para utilizarlo.
Crea una nueva aplicación llamada `mathapp`.
```
cd $GOPATH/src
mkdir mathapp
cd mathapp
vim main.go
```
código
```
//$GOPATH/src/mathapp/main.go código fuente
package main
@@ -83,23 +83,23 @@ código
func main() {
fmt.Printf("Hello, world. Sqrt(2) = %v\n", mymath.Sqrt(2))
}
```
Para compilar esta aplicación necesitas cambiar al directorio de la aplicación que en este caso es `$GOPATH/src/mathapp`, luego ejecuta el comando `go install`. Ahora deberías ver un archivo ejecutable llamado `mathapp` que se ha generado en el directorio `$GOPATH/bin/`. Para correr este programa usa el comando `./mathapp`. Deberías de ver el siguiente contenido en tu terminal:
```
Hello world. Sqrt(2) = 1.414213562373095
```
## Instala paquete remotos
Go tiene una herramienta para instalar paquetes remotos, es el comando llamado `go get`. Soporta la mayoría de comunidades de código libre, incluyendo Github, Google Code, BitBucket y Launchpad.
```
go get github.com/astaxie/beedb
```
Puedes usar `go get -u …` para actualizar tus paquetes remotos e incluso instalará todas sus dependencias.
Esta herramienta usará diferente herramientas de control de versiones para las diferentes plataformas de código libre. Por ejemplo, `git` para Github y `hg` para Google Code. Debido a esto, debes instalar estas herramientas de control de versiones antes de usar `go get`.
Después de ejecutar los comandos anteriormente descritos, la estructura de directorios debería verse de la siguiente forma:
```
$GOPATH
src
|-github.com
@@ -110,7 +110,7 @@ Después de ejecutar los comandos anteriormente descritos, la estructura de dire
|-github.com
|-astaxie
|-beedb.a
```
Actualmente, `go get` clona el código fuente a la ruta $GOPATH/src, luego ejecuta `go install`.
Puedes usar paquetes remotos de la misma forma que usas paquetes locales.
@@ -120,7 +120,7 @@ Puedes usar paquetes remotos de la misma forma que usas paquetes locales.
## Estructura completa del directorio
Si has seguido todos los pasos anteriores, la estructura de tu directorio se debería ver de la siguiente forma.
```
bin/
mathapp
pkg/
@@ -139,13 +139,13 @@ Si has seguido todos los pasos anteriores, la estructura de tu directorio se deb
beedb/
beedb.go
util.go
```
Ahora puedes ver la estructura de directorios claramente; `bin` contiene los archivos ejecutables, `pkg` contiene los archivos compliados y `src` los archivos de código fuente del paquete.
(El formato de las variables de entorno en Windows es `%GOPATH%`, sin embargo este libro sigue principalmente el estilo Unix, así que si eres un usuario Windows debes reemplazarlo apropiadamente)
## Links
- [Directorio](preface.md)
- [Índice](preface.md)
- Sección anterior: [Instalación](01.1.md)
- Sección siguiente: [Comandos Go](01.3.md)

View File

@@ -31,7 +31,7 @@ Este comando es para pruebas de compilación. Compilará paquetes dependientes d
## go clean
Este comando es para limpiar los archivos que son generados por los compiladores, incluyendo los siguientes archivos:
```
_obj/ // viejo directorio de object, dejado por Makefiles
_test/ // viejo directorio de test, dejado por Makefiles
_testmain.go // viejo directorio de gotest, dejado por Makefiles
@@ -42,7 +42,7 @@ Este comando es para limpiar los archivos que son generados por los compiladores
DIR(.exe) // generado por go build
DIR.test(.exe) // generado por go test -c
MAINFILE(.exe) // generado por go build MAINFILE.go
```
Usualmente utilizo este comando para limpiar mis archivos antes de subir mi proyecto a Github. Estas son herramientas útiles para pruebas locales, pero inútiles para el control de versiones.
## go fmt
@@ -54,12 +54,12 @@ Usualmente usamos `gofmt -w` en vez de `go fmt`. El último, no rescribirá tus
## go get
Este comando es para obtener paquetes remotos. Hasta el momento soporta BitBucket, Github, Google Code y Launchpad. Actualmente existen dos cosas que suceden después de ejecutar este comando. La primera es que Go descarga el código fuente, luego ejecuta `go install`. Antes de que utilices este comando, asegúrate que tienes instaladas todas las herramientas relacionadas.
```
BitBucket (Mercurial Git)
Github (git)
Google Code (Git, Mercurial, Subversion)
Launchpad (Bazaar)
```
Para poder usar este comando, debes instalar estas herramientas correctamente. No olvides establecer `$PATH`. Por cierto, también soporta nombres de dominios customizados. Usa `go help remote` para más detalles al respecto.
## go install
@@ -69,12 +69,12 @@ Este comando compila todos los paquetes y genera archivos, luego los mueve a `$G
## go test
Este comando carga todos los archivos cuyos nombres incluyen `*_test.go` y genera archivos de pruebas, luego muestra información que se ve de la siguiente forma.
```
ok archive/tar 0.011s
FAIL archive/zip 0.022s
ok compress/gzip 0.033s
...
```
Prueba todos tus archivos de prueba por defecto. Usa el comando `go help testflag` para más detalles.
## go doc
@@ -88,17 +88,17 @@ Ejecuta el comando `godoc -http=:8080`, luego abre `127.0.0.1:8080` en tu navega
## Otros comandos
Go provee comandos adicionales a los que ya mostramos.
```
go fix // actualiza código de una vieja versión antes de go1 a una nueva versión después de go1
go version // muestra información de tu versión de Go
go env // muestra las variables de entorno relacionadas a Go
go list // lista todos los paquetes instalados
go run // compila los archivos temporales y ejecuta la aplicación
```
También hay más detalles de los comandos que hemos hablado, puedes usar el comando `go help <nombre de comando>` para mostrarlos.
## Links
- [Directorio](preface.md)
- [Índice](preface.md)
- Sección anterior: [$GOPATH y el ambiente de trabajo](01.2.md)
- Sección siguiente: [Herramientas de Desarrollo para Go](01.4.md)

View File

@@ -54,14 +54,14 @@ Características de LiteIDE
- Instala gocode
Debes instalar gocode para poder usar el completamiento inteligente
```
go get -u github.com/nsf/gocode
```
- Entorno de compilación
Cambia la configuración en LiteIDE para que se ajuste a tu sistema operativo.
En Windows y usando al versión de 64-bits de Go, debes usar win64 cómo la configuración de entorno en la barra de herramientas. Luego escoge `Options`, busca `LiteEnv` en la lista de la izquierda y abre el archivo `win64.env` en la lista de la derecha.
```
GOROOT=c:\go
GOBIN=
GOARCH=amd64
@@ -70,11 +70,11 @@ Características de LiteIDE
PATH=%GOBIN%;%GOROOT%\bin;%PATH%
。。。
```
Reemplaza `GOROOT=c:\go` con tu ruta de instalación de Go y guarda. Si tienes MinGW64, agrega `c:\MinGW64\bin` a la variable de entorno de tu path para soportar `cgo`.
En Linux y usando la versiónd de 64-bits de Go, debes escoger linux64 como la configuración de entorno en la barra de herramientas. Luego escoge `Options`, busca `LiteEnv` en la lista de la izquierda y abre el archivo `linux64.env` en la lista de la derecha.
```
GOROOT=$HOME/go
GOBIN=
GOARCH=amd64
@@ -83,7 +83,7 @@ Características de LiteIDE
PATH=$GOBIN:$GOROOT/bin:$PATH
。。。
```
Reemplaza `GOROOT=$HOME/go` con tu ruta de instalación de Go y guarda.
- $GOPATH
$GOPATH es la ruta que contiene la lista de proyectos. Abre la herramienta de comandos (o presiona `Ctrl+` en LiteIDE), luego escribe `go help gopath` para más detalles.
@@ -141,22 +141,22 @@ Vim es un editor de texto popular para los programadores, que evolucionó de su
Imagen 1.8 Completamiento inteligente en Vim para Go
1. Realce de sintaxis para Go
```
cp -r $GOROOT/misc/vim/* ~/.vim/
```
2. Habilitando el realce de sintaxis
```
filetype plugin indent on
syntax on
```
3. Instalar [gocode](https://github.com/nsf/gocode/)
```
go get -u github.com/nsf/gocode
```
gocode se instalará por defecto en `$GOBIN`
4. Configura [gocode](https://github.com/nsf/gocode/)
```
~ cd $GOPATH/src/github.com/nsf/gocode/vim
~ ./update.bash
~ gocode set propose-builtins true
@@ -166,7 +166,7 @@ Imagen 1.8 Completamiento inteligente en Vim para Go
~ gocode set
propose-builtins true
lib-path "/home/border/gocode/pkg/linux_amd64"
```
Explicación de la configuración de gocode:
propose-builtins: especifica si abrir o no el completamiento inteligente; falso por defecto.
@@ -183,16 +183,16 @@ Emcas es la llamada arma de Dios. No es solamente un editor, sino un poderoso ID
Imagen 1.10 Panel principal de Emacs editando Go
1. Realce de sintaxis
```
cp $GOROOT/misc/emacs/* ~/.emacs.d/
```
2. Instalar [gocode](https://github.com/nsf/gocode/)
```
go get -u github.com/nsf/gocode
```
gocode se instalará por defecto en `$GOBIN`
3. Configura [gocode](https://github.com/nsf/gocode/)
```
~ cd $GOPATH/src/github.com/nsf/gocode/vim
~ ./update.bash
~ gocode set propose-builtins true
@@ -202,14 +202,14 @@ Imagen 1.10 Panel principal de Emacs editando Go
~ gocode set
propose-builtins true
lib-path "/home/border/gocode/pkg/linux_amd64"
```
4. Instalar [Auto Completion](http://www.emacswiki.org/emacs/AutoComplete)
Descarga y descomprime
```
~ make install DIR=$HOME/.emacs.d/auto-complete
```
Configura el archivo ~/.emacs
```
;;auto-complete
(require 'auto-complete-config)
(add-to-list 'ac-dictionary-directories "~/.emacs.d/auto-complete/ac-dict")
@@ -217,10 +217,10 @@ Imagen 1.10 Panel principal de Emacs editando Go
(local-set-key (kbd "M-/") 'semantic-complete-analyze-inline)
(local-set-key "." 'semantic-complete-self-insert)
(local-set-key ">" 'semantic-complete-self-insert)
```
Visita este [link](http://www.emacswiki.org/emacs/AutoComplete) para más detalles.
5. Configura .emacs
```
;; golang mode
(require 'go-mode-load)
(require 'go-autocomplete)
@@ -305,6 +305,7 @@ Imagen 1.10 Panel principal de Emacs editando Go
(interactive)
(show-all)
(shell-command-on-region (point-min) (point-max) "go tool fix -diff"))
```
6. Felicitaciones, ya estás listo! Speedbar está cerrada por defecto -remueve los símbolos de comentarios en la línea `;;(speedbar 1)` para habilitar esta característica o puedes usarla a través de `M-x speedbar`.
## Eclipse
@@ -327,9 +328,9 @@ Imagen 1.1 Panel principal de Eclipse editando Go
Necesitas instalar git en Windows, usualmente usamos [msysgit](https://code.google.com/p/msysgit/)
Instala gocode en la herramienta de comandos
```
go get -u github.com/nsf/gocode
```
También puedes instalar desde el código fuente si gustas.
4. Descarga e instala [MinGW](http://sourceforge.net/projects/mingw/files/MinGW/)
5. Configura los plugins.
@@ -390,10 +391,10 @@ Las personas que han trabajado con Java deben estar familiarizadas con este IDE.
Introduce la ubicación de tu Go sdk en el siguiente paso - básicamente es tu $GOROOT.
( ***Revisa este [artíclo de blog](http://wuwen.org/tips-about-using-intellij-idea-and-go/) para una configuración paso a paso *** )
(**Revisa este [artíclo de blog](http://wuwen.org/tips-about-using-intellij-idea-and-go/) para una configuración paso a paso** )
## Links
- [Directorio](preface.md)
- [Índice](preface.md)
- Sección anterior: [Comandos Go](01.3.md)
- Sección siguiente: [Resumen](01.5.md)

View File

@@ -4,6 +4,6 @@ En este capítulo hablamos acerca de como instalar Go usando tres métodos difer
## Links
- [Directorio](preface.md)
- [Índice](preface.md)
- Sección anterior: [Herramientas de Desarrollo para Go](01.4.md)
- Siguiente capítulo: [Conocimiento básico de Go](02.0.md)

17
es/02.0.md Normal file
View File

@@ -0,0 +1,17 @@
# 2 Go, Conocimiento básico
Go es un lenguaje de progrmación compilado y pertenece a la familia de C. Sin embargo su velocidad de compilación es mayor que otros lenguajes de la familia de C. Tiene únicamente 25 palabras reservadas ... ¡Incluso menos que las 26 letras del alfabeto Inglés! Vamos a echarle un vistazo a a estas palabras reservadas antes de comenzar
break default func interface select
case defer go map struct
chan else goto package switch
const fallthrough if range type
continue for import return var
En este capítulo voy a enseñarles algún conocimiento básico de Go. Encontrarás que concizo es el lenguaje de programación Go y la belleza del diseño del lenguaje. Programar puede ser muy dvertido en Go. Despuś de completar este capítulo, estarás familiarizado con las palabras reservadas de arriba.
## Enlaces
- [Directorio](preface.md)
- Capítulo anterior: [Capítulo 1 Resumen](01.5.md)
- Siguiente sección: ["Hola, Go"](02.1.md)

103
es/02.1.md Normal file
View File

@@ -0,0 +1,103 @@
## ¿Qué hace a Go diferente de otros lenguajes
El lenguaje de programación Go fue creado con una meta en mente, ser capaz de construir aplicaciones web escalables para grandes audiencias ocn un equipo grande. Esta es la razón por la cual ellos hicieron el lenguaje tan estandarizado como fuera posible, por esto razón la herramienta `gofmt` y el estricto uso de las guías base del lenguaje fueron creados por el motivo de no tener dos caras en la base del desarrollo, en otros lenguajes existen guerras religiosas en ¿Dónde debo colocar la llave abierta?
```
public static void main() {
}
or
public static void main()
{
}
```
O para python debo usar 4 espacios, 6 espacios o tabulación y otras preferencias de usuario.
Esto puede parecer ser un problema superficial en la superficie, pero cuando la base de código crece y muchas mas personas están trabajando en la misma base de código, entonces es dificil mantener la "belleza" del código. Si usted conoce python, entonces usted debe ser conciente del PEP8, el cual es un conjunto de guías sobre como escribir código elegantemente. Vivimos en un mundo donde los robots pueden conducir un carro, entonces nosotros no solo debemos escribir código, debemos escribir código elegante.
Para otros lenguajes existen muchas variables cuando es hora de escribir código, cada lenguaje es bueno para su caso de uso, pero Go es un caso especial en esa superficie porque fue diseñado por una compañia la cual es sinónimo de Internet (y de la computación distribuida), tipicamente el flujo de escribir código va de Python a Java a C++ para propósitos de optimización, pero el problema es que casi todos los lenguajes de programación fueron escritos décadas atrás, cuando 1GB de almacenamiento tenía un costo mucho mas alto comparado con hoy en día, donde el costo de almacenamiento y computación se ha vuelto mas económico. Los computadores se están volviendo multinúcleo estos días y los "lenguajes viejos" no aprovechan la concuerrencia de la manera que Go lo hace, no porque esos lenguajes sean malos, simplemente porque el caso de uso no era reelevante cuando esos lenguajes evolucionaron.
Entonces para mitigar todos los problemas que Google enfrentó con las herramientas actuales, ellos escribieron un sustema de lenguaje llamado Go, ¡el cual estás a punto de aprendeer! Hay muchas ventajas para usar Golang, y también muchas desventajas, para cada moneda hay dos caas. Pero hay avances significativos en lugares donde el formato de código, desde que llos diseñaron el lenguaje en una manera que no habrían guerrras con como darle formato al código. El código de formato de Go, escrito por cualquier persona en el mundo (asumiendo que usan `gofmt`) se verá exactamente igual, ¡esto parecerá no importar hasta que trabajes en un equipo! también cuando una compañia usa revisión de código automatizada o alguna otra técnica lujosa, donde en otros lenguajes que no tienen un estándar en las reglas de formato, todo el código estará comprometido, ¡pero no en Go.
Go fue diseñado con la concurrencia en mente, por favor note que paralelismo != concurrencia. Hay un post maravilloso escrito por Bob Pike en el blog de Go, blog.golang.org, que usted encontraréa ahí y vale la pena leerlo.
Otro cambio muy importante que Go trajo a la programación y que yo personalmente amo es le concepto de `GOPATH`, los tiempos donde tenías que crear una carpeta llamadacode y entonces crear espacios de trabajo para eclipse y otros. Ahora usted puede tener un árbol de carpetas para el código en Go y se mantendrá actualizado automáticamente por el administrador de paquetes. También bajo el código que estamos recomendando crear carpetas con cada dominio específico o el dominio de github, por ejemplo yo creé un manejador de tareas usando Go, entonces creé un conjunto de carpetas
`~/go/src/github.com/thewhitetulip/Tasks` Nota: En sistemas * nix `~` se refiere al directorio del usuario, que en windows es equivalente a `C:\\Users\\username`
Ahora el `~/go/` es el universo de código Go en su máquina, este es solo una significante mejora en comparación con otros lenguajes, entonces podemos almacenar código eficientemente sin molestias, esto puede parecer raro al comienzo pero tiene un montón de sentido a comparación de los nombres de paquete en otros lenguajes que usan sistemas como el del dominio inverso.
nota: junto con el src, existen dos carpetas adicionales, que son el `pkg` y el `bin` que es para los archivos binarios
Estas ventajas de `GOPATH` no están restringidas a guardar código en una carpeta particular, pero cuando tu tienes creado 5 paquetes para tu proyecto entonces tu no vas a tener tiempo de importarlos como `import ./db`, usted puede escribir `import "github.com/thewhitetulip/Tasks/db"`, entonces esto hará un `go get` a mi repositorio y la herramienta `go` encontrará el paquete de `github.com/...` si no estaba descargado inicialmente, eso estandariza un montón de cosas en la disciplina de la programación.
Algunos se quejan que los creadores de go han ignorado toda la investigación de lengaujes hecha los pasados 30 años, bueno, esto puede ser verdad, pero de nuevo, no puedes crear un lenguaje que todo mundo ame, siempre hay alguna u otra causa o restricción que hace a los creadores considerar, y considerando todas las ventajas al menos para el desarrollo web, yo no creo que algún lenguaje se acerque tanto a las ventajas que ha alcanzado `Go`, incluso si ignoras todo lo que he dicho arriba, Go es u lenguaje compilado, lo que significa que en producción no vas a tener que configurar un `JVM` o un `virtualenv`, ¡solo vas a tener un un estático binario! y como un cubo de hielo en un pastel, todas las librerías modernas están en la librería estándar, como `http`, lo cual es una ventaja mayor, es la razón principal por la que puedes crear aplicaciones web en Go sin tener que usar un framework de terceros.
# 2.1 Hola, Go
Antes de comenzar a contruir una aplicación en Go, necesitamos aprender como escribir un simple programa. No puedes esperar construir un edificio sin saber como construir sus fundamentos. Por esta razón, vamos a aprender la sintaxis básica para correr algunos programas en esta sección.
## Programa
De acuerdo con la prácica internaciona, antes de que tu puedas contruir en algún lenguaje, usted querrá escribir un programa que imporima "Hola Mundo".
¿Estamos listos? ¡Empecemos!
```
package main
import "fmt"
func main() {
fmt.Printf("Hola, mundo o 你好,世界 o καλημ ́ρα κóσμ o こんにちは世界\n")
}
```
Esto imprime la siguiente información
```
Hola, mundo o 你好,世界 o καλημ ́ρα κóσμ o こんにちは世界
```
## Explanation
Una cosa que usted debería tener en cuanta es que los programas en Go están compuestos por `package`s o paquetes.
`package <nombreDelPaquete>` (En este caso es `package main`) nos dice que el archivo pertenece al paquete `main` y la palabra main nos dice que este paquete será compilado a un programa en vez de a un archivo cuya extensión es `.a`.
Cada programa ejecutable tendrá un solo paquete `main` y una única función de enrtada llamada `main`sin ningún argumento de entrada o salidad en el paquete `main`.
En orden de imprimir `Hola, mundo ...` llamamos a una función llamada `Printf`. Esta funcieon viene del paquete `fmt`, entonces importamos este paquete en la tercera línea de código, que es `import "fmt"`
La manera de pensar sobre estos paquetes es similar a Python, y hay muchas otras ventajas: Modularidad (dividir tu programa en muchos módulos) y reusabilidad (cada módulo puede ser reusado en muchos programas). Nosotros solo hemos hablado sobre los conceptos relacionado con los paquetes, y luego daremos un paseo por nuestros propios paquetes, después.
En la quinta línea, usamos la palabra reservada `func` para definir la función `main`. El cuerpo d ela función está dentro de `{}`, como en C, C++ o Java.
Como puedes ver, no hay argumentos. Aprenderemos como escribir una función con argumentos en un segundo, y también hay funciones que no retornan nada o que retornan muchos valores.
En la sexta lína, llamamos la la funcieon `Printf` la cual pertenece al paquete `fmt`. Esta fue llamada por la sintaxis `<nombreDelPaquete>.<nombreDeLaFunción>`, que es mucho el estilo de Python.
Como mencionamos en el capítulo 1, el nombre de paquete y el nombre de la carpeta que contiene el archivo puede ser diferente. Aquí el `nombreDelPaquete` viene del nombre en `package <nombreDelPaquete>`, no del nombre de la carpeta.
Usted se puede dar cuenta que el ejemplo de arriba contiene catacteres no ASCII. El propósito de mostrar esto es para decir que Go soporta UTF-8 por defecto. Usted puede usar cualquier caracter UTF-8 en sus programas.
Cada archivo en Go está en algún pauqete, y el paquete debería estar en una carpeta distina en el GOPATH, pero el paquete `main` no requiere estar en una carpeta llamada `main`. ¡Este es un aspecto que ellos dejaron fuera de la estandarización! Pero si usted escoge crear una carpeta llamada main, tiene que asegurarse de ejecutar el binario correctamente. Un programa en go no puede tener mas de 1 archivo main.
`~/go/src/github.com/thewhitetulip/Tasks/main $ go build`
`~/go/src/github.com/thewhitetulip/Tasks $ ./main/main`
El detalle aquí es que cuando está usando algun archivo estático o algo mas, entonces vas a tener que ejecutar el archivo desde la raiz de la aplicación, como se ve en la segunda línea de arriba. Estoy ejecutando el binario `main` *fuera* del paquete main, en algún momento notarás que nuestra aplicación no está funcionando de la manera que queremos, por esta razón, para evitar problemas, mantenga esto en mente.
Una caosa que usted puede notar es que Go no usa punto y comas para terminar una sentencia, bueno, si lo hace, solo que el usuario no tiene que preocuparse por colocar los punto y comas, el compilador agregas los punto y comas al final del gocode, por esta razón esto es un error de sintaxis
```
func main ()
{
}
```
porque el compilador coloca un punto y coma al final de `main()` lo cual es un error de sintaxis , y esto ayuda a evitar guerras religiosas. Espero que combinen `vim` y `emacs` y creen un editor universal que nos ayude a evitarnos mas guerras, pero por ahora vamos a concentrarnos en aprender Go.
## Conclusión
Go usa `package` (como lo módulos en Python) para organizar los programas. La función `main.main()` (esta funcieon debe estar en el paquete `main`) es el punto de entrada de cualquier programa. Go estandariza el lenguaje y la mayoría de la metodología de programación, ahorrándole tiempo a los desarrolladores que se pierden en guerras religiosas. Solo puede existir un paquete main y solo una función main dentro del paquete main de Go. Go soporta caracteres UTF-8 porque uno de los creadores de Go es el creador de UTF-8, así que Go soporta múltiples lenguajes desde el momento en que nació.
## Enlaces
- [Índice](preface.md)
- Sección anterior: [Go, Conocimiento básico](02.0.md)
- Siguiente sección: [Fundamentos de Go](02.2.md)

478
es/02.2.md Normal file
View File

@@ -0,0 +1,478 @@
# 2.2 Principios de Go
En esta sección, vamos a aprender como definir constantes, variables con tipos básicos y algunos conocimientos mas de la programación en Go.
## Definición de variables
Tenemos muchas formas diferentes de definir variables con la sintaxis de Go.
Utilizando la palabra reservada `var` es la forma básica de definir una variable, observe como en Go se coloca el tipo de variable luego del nombre de la misma.
```
// definición de una variables de nombre “variableName” y tipo "type"
var variableName type
```
Definición de múltiples variables.
```
// definimos tres variables donde su tipo es "type"
var vname1, vname2, vname3 type
```
Definición de una variable con un valor inicial.
```
// definición de una variable de nombre “variableName”, tipo "type" y valor "value"
var variableName type = value
```
Definición de múltiples variables con valores iniciales.
```
/*
Definición de tres variables de tipo "type", y sus valores iniciales.
vname1 de valor v1, vname2 de valor v2, vname3 de valor v3
*/
var vname1, vname2, vname3 type = v1, v2, v3
```
¿Crees que es muy tedioso utilizar las anteriores formas para definir variables? Entonces no te preocupes, porque el equipo de desarrollo de Go también se encontró con este problema.
Por lo que si deseas definir una variable con valores iniciales, podemos omitir el tipo de cada una,
por lo que el código quedaría algo así:
```
/*
Definición de tres variables de tipo "type", y sus valores iniciales.
vname1 de valor v1vname2 de valor v2vname3 de valor v3
*/
var vname1, vname2, vname3 = v1, v2, v3
```
Bueno, se que aun no es lo suficientemente sencillo. Vamos a ver como lo mejoramos.
```
/*
Definición de tres variables de tipo "type", y sus valores iniciales.
vname1 de valor v1vname2 de valor v2vname3 de valor v3
*/
vname1, vname2, vname3 := v1, v2, v3
```
Ahora se ve mucho mejor. Usa `:=` para reemplazar a `var` y `type`, esto se llama declaración breve. Pero espera, esto tiene una limitante, esta forma de declaración solo puede ser utilizada dentro de una función. Si intentas utilizarla fuera del cuerpo de una función vas a recibir un error de compilación. Por lo que normalmente utilizamos `var` para definir variables globales, y podemos utilizar la declaración breve en `var()`.
`_` (guión bajo) es un nombre especial de variable, cualquier valor que le sea otorgado será ignorado. Por ejemplo, le otorgamos `35` a `b`, y descartamos el `34`.( ***Este ejemplo nos muestra como esto funciona. Parece inútil aquí porque normalmente lo utilizamos cuando tomamos los valores de retorno de una función.*** )
```
_, b := 34, 35
```
Si no utilizas alguna de las variables declaradas en el programa, el compilador les lanzara un error. Intenta compilar el siguiente código, ve que sucede.
```
package main
func main() {
var i int
}
```
## Constantes
Las llamadas constantes son los valores que se determinan en el momento de compilación, y que no se pueden modificar durante la ejecución. En Go, podemos utilizar números, booleanos o cadenas como tipos de constantes.
Definimos una constante de la siguiente manera.
```
const nombreConstante = valor
// podemos asignar el tipo de una constante si es necesario
const Pi float32 = 3.1415926
```
Otros ejemplos.
```
const Pi = 3.1415926
const i = 10000
const MaxThread = 10
const prefix = "astaxie_"
```
## Tipos básicos
### Booleanos
En Go, podemos usar `bool` para definir variables de tipo booleanas, su valor puede ser unicamente `true` o `false`, y `false` sera el valor por defecto. ( ***No puede convertir el tipos variables entre números y booleanos!*** )
```
// ejemplo de código
var isActive bool // variable global
var enabled, disabled = true, false // omitimos el tipo de las variables
func test() {
var available bool // variable local
valid := false // definición breve de una variable
available = true // asignación de un valor a una variable
}
```
### Tipos Numéricos
Tipos enteros incluyendo los enteros con signo y sin signo. Para esto Go tiene `int` y `uint` al mismo tiempo, ellos tienen la misma longitud, pero la longitud específica va a depender de su sistema operativo. Ellos utilizan 32-bits en sistemas operativos de 32-bits, y 64-bits en sistemas operativos de 64-bits. Go también tiene tipos que tienen una longitud específica incluyendo `rune`, `int8`, `int16`, `int32`, `int64`, `byte`, `uint8`, `uint16`, `uint32`, `uint64`. Tenga en cuenta que `rune` es el alias de `int32` y `byte` es el alias para `uint8`.
Algo importante que debes saber es que no puede asignar valores entre estos tipos, esta operación va a causar un error de compilación.
```
var a int8
var b int32
c := a + b
```
Aunque int32 tiene una longitud mayor que uint8, y tiene la misma longitud que int, pero no podrá asignar valores entre ellos. ( ***aquí c afirmará ser de tipo `int`*** )
Los tipos Float tienen `float32` y `float64`, y no tienen un tipo llamado `float`, este último solo es el tipo por defecto cuando se utiliza la declaración breve.
¿Eso es todo? ¡No! Go también tiene número complejos. `complex128` (con una parte de 64-bits reales y otra parte de 64-bits imaginarios) es el tipo por defecto, si necesitas un tipo mas pequeño, hay uno llamado `complex64` (con una parte de 32-bits reales y otra de 32-bits imaginarios). Su forma es `RE+IMi`, donde `RE` es la parte real e `IM` es la parte imaginaria, el último `i` es el número imaginario. Este es un ejemplo de número complejo.
```
var c complex64 = 5+5i
//salida: (5+5i)
fmt.Printf("Value is: %v", c)
```
### Cadenas
Acabamos de hablar sobre que Go utiliza el juego de caracteres de UTF-8. Las Strings son representadas mediante comillas dobles `""` o comillas simples ``` `` ```.
```
// ejemplo de código
var frenchHello string // forma básica de definir una cadena
var emptyString string = "" // definimos una cadena con un valor vacío
func test() {
no, yes, maybe := "no", "yes", "maybe" // declaración breve
japaneseHello := "Ohaiou"
frenchHello = "Bonjour" // forma básica para asignar un valor
}
```
Es imposible cambiar el valor de una cadena por su índice. Vas a obtener un error cuando compiles el siguiente código.
```
var s string = "hello"
s[0] = 'c'
```
Pero, y si ¿realmente deseo cambiar el valor de un solo carácter de una string? Intenta utilizar el siguiente código.
```
s := "hello"
c := []byte(s) // convertimos una string a un tipo []byte
c[0] = 'c'
s2 := string(c) // volvemos a convertirlo a un tipo string
fmt.Printf("%s\n", s2)
```
Podemos utilizar el operador `+` para combinar dos cadenas.
```
s := "hello,"
m := " world"
a := s + m
fmt.Printf("%s\n", a)
```
y también.
```
s := "hello"
s = "c" + s[1:] // no puede cambiar los valores de una cadena por su índices pero puedes obtener sus valores.
fmt.Printf("%s\n", s)
```
Que pasa si ¿quiero tener una string de varias líneas?
```
m := `hello
world`
```
` `` ` capturará todos los caracteres en una cadena.
### Tipos de Errores
Go tiene un tipo de `error` para hacer frente a los mensajes de error. También tiene un paquete llamado `errors` para manejar los errores.
```
err := errors.New("emit macho dwarf: elf header corrupted")
if err != nil {
fmt.Print(err)
}
```
### Estructura de datos fundamental
La siguiente imagen viene de un artículo sobre [Estructuras de datos en Go](http://research.swtch.com/godata) en [El Blog de Russ Cox](http://research.swtch.com/). Como puedes ver, Go nos da bloques de memoria para almacenar datos.
![](images/2.2.basic.png?raw=true)
Imagen 2.1 Go estructura de datos fundamental
## Algunas facilidades
### Definir por grupo
Si deseas definir múltiples constantes, variables o importaciones de paquetes, podemos utilizar formularios de grupos.
Forma básica.
```
import "fmt"
import "os"
const i = 100
const pi = 3.1415
const prefix = "Go_"
var i int
var pi float32
var prefix string
```
Forma de grupo.
```
import(
"fmt"
"os"
)
const(
i = 100
pi = 3.1415
prefix = "Go_"
)
var(
i int
pi float32
prefix string
)
```
A menos que asignes el valor de la constante `iota`, el primer valor de la constante en el grupo `const()` va a ser `0`. Si a las siguientes constantes no se les asignan valores explícitamente, sus valores serán el mismo que el último. Si el último valor de la constante es `iota`, los valores de las siguientes constantes que no son asignadas también serán `iota`.
### Enumerar con iota
Go tiene la palabra reservada `iota`, esta palabra reservada va a crear un `enum`, que va a comenzar con `0`, y se va a ir incrementando en `1`.
```
const(
x = iota // x == 0
y = iota // y == 1
z = iota // z == 2
w // Si no hay una expresión después del nombre de la contasnte, se usa la última expresión,
//entonces aquí nos dice que w = iota implícitamente. Por lo tanto w == 3, e y y x los dos, podrían omitir "= iota" de la misma forma.
)
const v = iota // cuando se vuelve a utilizar la palabra reservada `const` seguida de la asignación iota nuevamente, este reinicia su valor a `0`, por esto v = 0.
const (
e, f, g = iota, iota, iota // e=0,f=0,g=0 mismos valores de iota en una línea.
)
```
### Algunas reglas
La razón por la que Go es conciso es porque tiene algunos comportamientos por defecto.
- Cualquier variables que comience con mayúscula se va exportar como pública, de otra manera será una variable privada.
- Se usa la misma regla para funciones y constantes, no existen las palabras reservadas `public` o `private` en Go.
## Arreglos, segmentos, mapas
### Arreglos (array)
`array` es un arreglo obviamente, lo definimos de la siguiente forma.
```
var arr [n]type
```
en `[n]type`, `n` es la longitud del arreglo, `type` es el tipo de elementos que contiene. Como en otros lenguajes, utilizamos `[]` para obtener o establecer los elementos en el array.
```
var arr [10]int // un arraglo de tipo int
arr[0] = 42 // los arreglos arrancan su índice en 0
arr[1] = 13 // asignamos un valor a un elemento
fmt.Printf("El primer elemento es %d\n", arr[0])
// obtenemos el valor del elemento, este nos devolvera 42
fmt.Printf("El último elemento es %d\n", arr[9])
//este nos devolverá el valor por defecto del elemento número 10 en este array, que es 0 en este caso.
```
Debido a que la longitud es una parte del tipo del arreglo, `[3]int` y `[4]int` son diferentes tipos, por lo que no podemos cambiar la longitud del arreglos. Cuando utilizamos arreglos como argumentos, las funciones reciben copias en lugar de referencias a ellos! Si lo que busca es utilizar referencias, es posible que desee utilizar `slice` de las que vamos a hablar más adelante.
Es posible usar `:=` cuando definimos arrays.
```
a := [3]int{1, 2, 3} // define un arreglo de int con 3 elementos
b := [10]int{1, 2, 3}
// define arreglo de int con 10 elementos, y los tres primeros son asignados
// el resto utiliza el valor por defecto 0.
c := [...]int{4, 5, 6} // usamos `…` para reemplazar el número de la longitud, Go la va a calcular por ti.
```
Es posible que desee utilizar arreglos como elementos de un arreglo, vamos a ver como hacerlo.
```
// definimos un arreglo de dos dimensiones con dos elementos, y cada elemento tiene cuatro elementos.
arregloDoble := [2][4]int{[4]int{1, 2, 3, 4}, [4]int{5, 6, 7, 8}}
// podemos escribirlo de una forma más corta.
arregloSimple := [2][4]int{{1, 2, 3, 4}, {5, 6, 7, 8}}
```
Arreglo: estructuras de datos fundamentales.
![](images/2.2.array.png?raw=true)
Imagen 2.2 Relación de mapeo de arreglo multi-dimensional
### Segmentos (slice)
En muchas situaciones, un arreglo no es una buena elección. Por ejemplo, nosotros no sabemos cual va a ser el largo del arreglo cuando lo definimos, entonces vamos a necesitar un "arreglo dinámico". Este es llamado `slice` en Go.
Un `segmento` no es realmente un `arreglo dinámico`, es un tipo referenciado. Un `segmento` apunta a un `arreglo` subyacente, su declaración es similar a los `arreglos`, pero no necesitan una longitud.
```
// al igual que como definimos un arreglo, pero no le pasamos la longitud esta vez
var fsegmento []int
```
Entonces definimos un `segmento`, e inicializamos sus datos.
```
segmento := []byte {'a', 'b', 'c', 'd'}
```
Un `segmento` puede ser re-definido desde otro slice o array existente. Un `segmento` usa `arreglo[i:j]` para tomar sus valores, donde `i` es
el índice del comienzo y `j` es el índice del final, note que `arreglo[j]` no será segmentado, porque la longitud
del segmento es `j-i`.
```
// definimos un segmento con 10 elementos donde el tipo es byte
var ar = [10]byte {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'}
// definimos dos segmento de tipo []byte
var a, b []byte
// a apunta a los elementos del tercero al quinto elemento en el array ar.
a = ar[2:5]
// ahora tiene los elementos ar[2],ar[3] y ar[4]
// b es otro slice del array ar
b = ar[3:5]
// ahora b tiene los elementos ar[3] y ar[4]
```
Note la diferencia entre `segmento` y `arreglo` cuando los definimos. Nosotros usamos `[…]` para que Go
calcule la longitud pero usamos`[]` para definir unicamente un segmento.
La estructura de datos subyacente a un segmento.
![](images/2.2.slice.png?raw=true)
Imagen 2.3 Similitud entre slice y array
Los segmentos tienen algunas operaciones utiles.
- Un `segmento` comienza en 0, `ar[:n]` es igual a `ar[0:n]`
- El segundo índice sera la longitud del `slice` si lo omitimos, `ar[n:]` será igual a `ar[n:len(ar)]`.
- Se puede usar `ar[:]` para tomar todo el arreglo, la razón de esto se explica en las dos anteriores explicaciones.
Más ejemplos acerca de `segmentos`
```
// definimos un arreglo
var array = [10]byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'}
// definimos dos segmentos
var aSlice, bSlice []byte
// Algunas operaciones útiles
aSlice = array[:3] // es igual a aSlice = array[0:3] aSlice tiene los elementos a,b,c
aSlice = array[5:] // es igual a aSlice = array[5:10] aSlice tiene los elementos f,g,h,i,j
aSlice = array[:] // es igual a aSlice = array[0:10] aSlice tiene todos los elementos
// segmento desde segmento
aSlice = array[3:7] // aSlice tiene los elementos d,e,f,glen=4cap=7
bSlice = aSlice[1:3] // bSlice contiene aSlice[1], aSlice[2], entonces este tendrá los elementos e,f
bSlice = aSlice[:3] // bSlice contiene aSlice[0], aSlice[1], aSlice[2], entonces este tiene d,e,f
bSlice = aSlice[0:5] // bSlice se puede expandir, ahora bSlice contiene d,e,f,g,h
bSlice = aSlice[:] // bSlice tiene los mismos elementos que aSlice, que son d,e,f,g
```
Un `segmento` es un tipo de referencia, por lo que si uno se modifica entonces afectará al resto.
Por ejemplo, con los elementos de anteriores `aSlice` y `bSlice`, si se modifica el valor de algún elemento en `aSlice`,
`bSlice` será modificado también.
`slice` por definición es como una estructura, y contiene tres partes.
- Un puntero que apunta donde comienza el `segmento`.
- la longitud del `segmento`.
- Capacidad, la longitud de donde comienza el indice hacia donde termina el indice del `slice`.
```
Array_a := [10]byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'}
Slice_a := Array_a[2:5]
```
Los fundamentos de la estructura de datos del código anterior es el siguiente.
![](images/2.2.slice2.png?raw=true)
Imagen 2.4 Array información del slice
Hay algunas funciones integradas para los segmentos.
- `len` nos devuelve la longitud del `segmento`.
- `cap` nos devuelve la longitud máxima del `segmento`
- `append` añade uno o mas elementos al `segmento`, y nos devuelve el `segmento` .
- `copy` copia elementos de un segmento hacia otro, y nos devuelve el número de elementos que fueron copiados.
Atención: `append` va a cambiar el array al que apunta el `slice`, y afectará a los otros que apuntan al mismo array.
Ademas si no tiene una longitud suficiente para el slice (`(cap-len) == 0`), `append` nos va a devolver un nuevo array para este slice, en este punto,
los otros slices van a apuntan al anterior array nos serán afectados.
### Mapas (map)
Un `mapa` es como un diccionario en Python, lo usamos de la siguiente forma `mapa[claveTipo]tipoValor` para definirlo.
Vamos a ver algo de código, para configurar o tomar un valor de un `mapa` es como en un `segmento`, usamos la `llave` para ello, pero el indice en un `segmento` puede ser
solo de tipo entero, y en un `mapa` podemos usar mucho mas que eso, `entero`, `cadena`, o lo que quieras. Ademas, todos ellos
pueden utilizar `==` y `!=` para comparar valores.
```
// usamos string tipo de llave (key), int como el tipo de valor, y debemos usar `make` para inicializarlo.
var numbers map[string] int
// otra forma de definir un mapa
numbers := make(map[string]int)
numbers["one"] = 1 // asignamos el valor para la clave
numbers["ten"] = 10
numbers["three"] = 3
fmt.Println("El tercer número es: ", numbers["three"]) // tomamos el valor
// Esto imprime: El tercer número es: 3
```
Algunas cosas a tener en cuenta cuando usamos map.
- Un `mapa` está y es desordenado, cada vez que imprimamos un `mapa` vamos a obtener diferentes resultados. Es imposible obtener un valor por índice, debe usar la clave.
- Un `mapa` no tiene una longitud fija, es un tipo de referencia al igual que los `segmentos`.
- `len` también funciona con `mapas`, este devuelve el número de claves que tiene el map.
- Es muy sencillo modificar el valor de un elemento del `mapa`, simplemente usamos `numbers["one"]=11` para cambiar el valor que contiene la clave one a `11`.
Se puede usar la forma `clave:valor` para inicializar los valores del map, y `map` tiene internamente los métodos para verificar si la clave existe.
Utilice `delete` para borrar un elemento del `mapa`.
```
// Inicialice un mapa
rating := map[string]float32 {"C":5, "Go":4.5, "Python":4.5, "C++":2 }
// map nos devuelve dos valores. El segundo valor ok es false si la clave no
//existe true de otra forma.
csharpRating, ok := rating["C#"]
if ok {
fmt.Println("C# se encuentra en el map y su ranking es ", csharpRating)
} else {
fmt.Println("No tenemos un ranking asociado con C# en este map")
}
delete(rating, "C") // borramos el elemento con la clave "c"
```
Como se dijo anteriormente, `map` es un tipo por referencia, si dos `map`s apuntan a los mismos datos,
cualquier cambio van a afectar a ambos.
```
m := make(map[string]string)
m["Hello"] = "Bonjour"
m1 := m
m1["Hello"] = "Salut" // ahora el valor de m["hello"] es Salut
```
### make, new
`make` realiza la asignación de memoria para construir las estructuras como los `mapas`, `segmentos`, o `canales`, `new`
es para la reserva de memoria de cada tipo.
`new(T)` reservamos la memoria para el valor vacío del tipo `T`'s, devuelve la dirección de memoria, que es el valor del tipo `*T`. En los términos de Go,
este devuelve un puntero, que apunta al valor vacío del tipo `T`.
`new` devuelve punteros.
La función incorporada `make(T, args)` tiene un diferente efecto que `new(T)`, `make` puede ser usado para `segmentos`, `mapas`,
y `canales`, y nos devuelve un valor inicializado con valor inicial del tipo `T`. La razón para hacer esto es porque estos tres tipos
por debajo deben ser inicializados antes que los punteros a ellos. Por ejemplo, un `segmento` contiene punteros que por debajo apuntan a
un `arreglo`, longitud y capacidad. Antes de que esta información sea inicializada, un `segmento` es `nil`, entonces para un `segmento`, `mapa`y
`canal`, `make` inicializa los datos que tienen por debajo, y asigna algunos valores configurables.
`make` devuelve valores distinto de cero.
La siguiente imagen nos muestra como son diferentes `new` y `make`.
![](images/2.2.makenew.png?raw=true)
Imagen 2.5 Reserva de memoria de fondo de make y new
En cuando a valor cero, no significa valor vacío. Es el valor de las variables cuando no son asignados de forma manual, usualmente es 0, esta es la lista de algunos de estos valores.
```
int 0
int8 0
int32 0
int64 0
uint 0x0
rune 0 // el tipo actual de rune es int32
byte 0x0 // el tipo actual de byte es uint8
float32 0 // la longitud es 4 byte
float64 0 // la longitud es 8 byte
bool false
string ""
```
## Enlaces
- [Indice](preface.md)
- Sección anterior: ["Hello, Go"](02.1.md)
- Siguiente sección: [Sentencias de control y funciones](02.3.md)

516
es/02.3.md Normal file
View File

@@ -0,0 +1,516 @@
# 2.3 Sentencias de control y funciones
En esta sección, vamos a hablar sobre sentencias de control de flujo y operaciones con funciones en Go.
## Sentencias de control
El mayor invento en los lenguajes de programación es el control de flujo. Gracias a ellos, podemos utilizar algunas sentencias sencillas de control para representar lógicas complejas. Tenemos tres categorías, condicionales, controles de ciclos y saltos incondicionales.
### if
`if` es la más común de las palabras reservadas en sus programas. Si se cumplen las condiciones entonces realiza algo, si no realiza otra cosa o nada.
`if` no necesita paréntesis en Go.
```
if x > 10 {
fmt.Println("x es mayor que 10")
} else {
fmt.Println("x es menor que 10")
}
```
Lo más útil de `if` en Go es que puede tener una instrucción de inicialización antes de la sentencia de condición. El alcance o ámbito de las variables de inicialización que nosotros definimos en esta condición es unicamente dentro del bloque del `if`.
```
// inicializamos x, entonces verificamos si x es mayor que 10
if x := computedValue(); x > 10 {
fmt.Println("x es mayor que 10")
} else {
fmt.Println("x es menor que 10")
}
// el siguiente código no va a compilar
fmt.Println(x)
```
Utiliza if-else para múltiples condiciones.
```
if entero == 3 {
fmt.Println("entero es igual a 3")
} else if entero < 3 {
fmt.Println("entero es menor que 3")
} else {
fmt.Println("entero es mayor que 3")
}
```
### goto
Go la palabra reservada `goto`, se cuidadoso cuando la usas. `goto` tiene un salto hacia la `etiqueta` en el cuerpo de el mismo bloque de código.
```
func myFunc() {
i := 0
Here: // label termina con ":"
fmt.Println(i)
i++
goto Here // salta hacia el label "Here"
}
```
El nombre de la etiqueta diferencia entre mayúsculas y minúsculas.
### for
`for` es la lógica de control mas poderosa en Go, puede leer los datos en los ciclos y realizar operaciones iterativas, al igual que un típico `while`.
```
for expression1; expression2; expression3 {
//...
}
```
`expression1`, `expression2` y `expression3` son obviamente todas expresiones, donde `expression1` y `expression3` son definiciones de variables o valores de retornos de funciones, y `expression2` es una sentencia condicional. `expression1` se ejecutara siempre antes de cada bucle, y `expression3` después.
Un ejemplo es mas útil que cientos de palabras.
```
package main
import "fmt"
func main(){
sum := 0;
for index:=0; index < 10 ; index++ {
sum += index
}
fmt.Println("la suma es igual a ", sum)
}
// Printsum es igual a 45
```
A veces necesitamos asignaciones múltiples, Go tiene el operador `,`, así que podemos usar la asignación paralela como `i, j = i + 1, j - 1`.
Si no son necesarios, podemos omitir a `expression1` y `expression3`.
```
sum := 1
for ; sum < 1000; {
sum += sum
}
```
Podemos omitir también el `;`. ¿Se siente familiar? Si, es un `while`.
```
sum := 1
for sum < 1000 {
sum += sum
}
```
Hay dos operaciones importantes en los ciclos que son `break` y `continue`. `break` salta afuera del ciclo, y `continue` salta el ciclo actual y continua en el siguiente. Si usted anida ciclos, utilice `break` para saltar al bucle que esta junto.
```
for index := 10; index>0; index-- {
if index == 5{
break // o continue
}
fmt.Println(index)
}
// break imprime 10、9、8、7、6
// continue imprime 10、9、8、7、6、4、3、2、1
`for` podría leer los datos desde un `segmento` o un `mapa` cuando es utilizado con `range`.
for k,v:=range map {
fmt.Println("llave del mapa:",k)
fmt.Println("valor del mapa:",v)
}
```
Como Go soporta el retorno de valores múltiples y nos da errores de compilación cuando utiliza valores que no fueron definidos, por eso puede necesitar utilizar `_` para descartar algunos valores de retorno.
```
for _, v := range map{
fmt.Println("valor del mapa:", v)
}
```
### switch
A veces puedes pensar que estas utilizando demasiados valores `if-else` para implementar alguna lógica, también puedes pensar que no se ve bien y que no sea correcto para mantener a futuro. Ahora es tiempo para utilizar `switch` para resolver este problema.
```
switch sExpr {
case expr1:
algunas instrucciones
case expr2:
algunas otras instrucciones
case expr3:
algunas otras instrucciones
default:
otro código
}
```
El tipo de `sExpr`, `expr1`, `expr2`, y `expr3` debe ser el mismo. `switch` es muy flexible, las condiciones no necesitan ser constantes, es ejecutado de arriba hacia abajo hasta que se cumpla alguna condición. Si no hay ninguna declaración después de la palabra reservada `switch`, entonces este se compara con `true`.
```
i := 10
switch i {
case 1:
fmt.Println("i es igual a 1")
case 2, 3, 4:
fmt.Println("i es igual a 2, 3 o 4")
case 10:
fmt.Println("i es igual a 10")
default:
fmt.Println("Todo lo que yo se, es que i es un entero")
}
```
En la quinta linea, pusimos muchos valores en un solo `case`, y no necesitamos utilizar `break` en el final del cuerpo de un `case`. Saltara fuera del cuerpo de switch una vez que coincida con algún case y ejecute las instrucciones dentro del mismo. Si usted busca que siga comparando con otros cases, va a necesitar utilizar la palabra reservada `fallthrough`.
```
integer := 6
switch integer {
case 4:
fmt.Println("integer <= 4")
fallthrough
case 5:
fmt.Println("integer <= 5")
fallthrough
case 6:
fmt.Println("integer <= 6")
fallthrough
case 7:
fmt.Println("integer <= 7")
fallthrough
case 8:
fmt.Println("integer <= 8")
fallthrough
default:
fmt.Println("default case")
}
```
Este programa va a imprimir la siguiente información.
```
integer <= 6
integer <= 7
integer <= 8
default case
```
## Funciones
Utilizamos la palabra reservada `func` para definir funciones.
```
func funcName(input1 type1, input2 type2) (output1 type1, output2 type2) {
// cuerpo de la función
// retorna múltiples valores
return value1, value2
}
```
Podemos obtener la siguiente información del ejemplo anterior.
- Utilizamos la palabra reservada `func` para definir la función llamada `funcName`.
- Funciones tienen cero, uno o mas de un argumento, el tipo del argumento después del nombre del mismo y separados por `,`.
- Las funciones pueden devolver múltiples valores.
- El ejemplo iene dos valores de retorno llamados `output1` y `output2`, se pueden omitir los nombre y utilizar unicamente los tipos.
- Si solo hay un valor de retorno y omite el nombre, no va a necesitar comas para retornar mas valores.
- Si la función no tiene valores de retorno, puede omitir la parte de retorno.
- Si la función tiene valores de retorno, va a necesitar utilizar `return` en alguna parte del cuerpo de la función.
Veamos un ejemplo práctico. (calcular el valor mínimo)
```
package main
import "fmt"
// devolvemos el valor mas grande entre a y b
func max(a, b int) int {
if a > b {
return a
}
return b
}
func main() {
x := 3
y := 4
z := 5
max_xy := max(x, y) // llama a la función max(x, y)
max_xz := max(x, z) // llama a la función max(x, z)
fmt.Printf("max(%d, %d) = %d\n", x, y, max_xy)
fmt.Printf("max(%d, %d) = %d\n", x, z, max_xz)
fmt.Printf("max(%d, %d) = %d\n", y, z, max(y,z)) // llamamos a la función aquí
}
```
En el ejemplo anterior, tenemos dos argumentos en la función `max`, los de tipo `int`, por eso el primer tipo puede ser omitido, como `a, b int` en lugar de `a int, b int`. Se cumple la misma regla para mas argumentos. Nótese que `max` tiene solo un valor de retorno, por lo que solo escribimos el tipo de valor de retorno, esta es una forma corta.
### Retorno de múltiples valores
Una de las cosas en las que Go es mejor que C es que soporta el retorno de múltiples valores.
Vamos a utilizar el ejemplo anterior aquí.
```
package main
import "fmt"
// retorna el resultado de A + B y A * B
func SumAndProduct(A, B int) (int, int) {
return A+B, A*B
}
func main() {
x := 3
y := 4
xPLUSy, xTIMESy := SumAndProduct(x, y)
fmt.Printf("%d + %d = %d\n", x, y, xPLUSy)
fmt.Printf("%d * %d = %d\n", x, y, xTIMESy)
}
```
En el ejemplo anterior devolvemos dos valores sin nombre, y tu también puedes nombrarlos. Si nombramos los valores de retorno, solo vamos a utilizar `return` para devolver para devolver bien los valores ya que se inicializan en la función automáticamente. Debes tener en cuenta que si tus funciones se van a utilizar fuera del paquete, lo que significa que los nombre de las funciones inician con mayúsculas, es mejor que escriba la sentencia mas completa para `return`; esto hace que es código sea mas legible.
```
func SumAndProduct(A, B int) (add int, Multiplied int) {
add = A+B
Multiplied = A*B
return
}
```
### Argumentos variables
Go soporta funciones con un número variable de argumentos. Estas funciones son llamadas "variadic", lo que significa que puede darle un numero incierto de argumentos a la función.
```
func myfunc(arg ...int) {}
```
`arg …int` le dice a Go que esta función tiene argumentos variables. Ten en cuenta que estos argumentos son de tipo `int`. En el cuerpo de la función , `arg` será un `segmento` de `enteros`.
```
for _, n := range arg {
fmt.Printf("Y el número es: %d\n", n)
}
```
### Pasando argumentos por valor y punteros
Cuando pasamos argumentos a una función a la cual llamamos, esa función actualmente toma una copia de nuestra variables, cualquier cambio que realicemos no va a afectar a la variable original.
Vamos a ver un ejemplo para probar lo que decimos.
```
package main
import "fmt"
// función sencilla para sumar 1 a 'a'
func add1(a int) int {
a = a+1 // cambiamos el valor de a
return a // devolvemos el nuevo valor de a
}
func main() {
x := 3
fmt.Println("x = ", x) // debe imprimir "x = 3"
x1 := add1(x) // llamamos a add1(x)
fmt.Println("x+1 = ", x1) // debe imprimir "x+1 = 4"
fmt.Println("x = ", x) // debe imprimir "x = 3"
}
```
¿Viste eso? Siempre que llamamos a `add1`, y `add1` le suma uno a `a`, el valor de `x` no sufre cambios.
El motivo de esto es muy sencillo: cuando llamamos a `add1`, nosotros obtenemos una copia de `x` para esto, no `x` en si mismo.
Ahora nos debemos preguntar, como puedo pasar el verdadero valor de `x` a la función.
Para eso necesitamos utilizar punteros. Sabemos que las variables son almacenadas en memoria, y todas ellas tienen una dirección de memoria, nosotros vamos a cambiar el valor de esa variables si cambiamos los valores en la dirección de memoria de esa variable. Por lo tanto la función `add1` tiene que saber la dirección de memoria de `x` para poder cambiar su valor. Por eso necesitamos pasar de la siguiente forma su valor `&x` a la función, y cambiar el tipo de argumento a uno de tipo puntero `*int`. Tiene que ser consciente de que pasamos una copia del puntero, no una copia del valor.
```
package main
import "fmt"
// función sencilla para sumar 1 a a
func add1(a *int) int {
*a = *a+1 // cambiamos el valor de a
return *a // devolvemos el nuevo valor de a
}
func main() {
x := 3
fmt.Println("x = ", x) // debe imprimir "x = 3"
x1 := add1(&x) // llamamos a add1(&x) pasando la dirección de memoria de x
fmt.Println("x+1 = ", x1) // debe imprimir "x+1 = 4"
fmt.Println("x = ", x) // debe imprimir "x = 4"
}
```
Ahora podemos cambiar el valor de `x` en la función. ¿Por qué usamos punteros? ¿Cuál es la ventaja?
- Usamos mas funciones que modifiquen una misma variable.
- Tiene un bajo costo utilizar direcciones de memoria (8 bytes), la copia no es una forma eficiente tanto en el tiempo como en el espacio para pasar variables.
- Las `cadenas`, `segmentos` y `mapas` son tipos de referenciados, por eso ellos por defecto utilizan punteros cuando se pasan a funciones. (Atención: si necesitas cambiar el tamaño de un `segmento`, vas a necesitar pasar el puntero de forma explicita)
### defer
Go tiene una palabra reservada muy bien diseñada llamada `defer`, puedes tener muchas declaraciones de `defer` en una función; ellas se ejecutan en orden inverso cuando el programa ejecuta el final de la función. Es útil especialmente cuando el programa abre un recurso como un archivo, estos archivos tienen que ser cerrados antes de que la función devuelva un error. Vamos a ver algún ejemplo.
```
func ReadWrite() bool {
file.Open("file")
// realizamos alguna tarea
if failureX {
file.Close()
return false
}
if failureY {
file.Close()
return false
}
file.Close()
return true
}
```
Vimos la repetición de código en varias ocasiones, `defer` nos va a resolver muy bien este problema. No solo nos va a ayudar a realizar código limpio, y también lo va a hacer mas legible.
```
func ReadWrite() bool {
file.Open("file")
defer file.Close()
if failureX {
return false
}
if failureY {
return false
}
return true
}
```
Si hay mas de un `defer`, ellos se van a ejecutar en orden inverso. El siguiente ejemplo va a imprimir `4 3 2 1 0`.
```
for i := 0; i < 5; i++ {
defer fmt.Printf("%d ", i)
}
```
### Funciones como valores y tipos
Las funciones son también variables en Go, podemos usar un `type` para definirlas. Las funciones que tienen la misma firma pueden verso como del mismo tipo.
```
type typeName func(input1 inputType1 , input2 inputType2 [, ...]) (result1 resultType1 [, ...])
```
¿Cuál es la ventaja de esta característica? La respuesta es que podemos pasar funciones como valores.
```
package main
import "fmt"
type testInt func(int) bool // definimos un función como un tipo de variable
func isOdd(integer int) bool {
if integer%2 == 0 {
return false
}
return true
}
func isEven(integer int) bool {
if integer%2 == 0 {
return true
}
return false
}
// pasamos la función `f` como un argumento de otra función
func filter(slice []int, f testInt) []int {
var result []int
for _, value := range slice {
if f(value) {
result = append(result, value)
}
}
return result
}
func main(){
slice := []int {1, 2, 3, 4, 5, 7}
fmt.Println("slice = ", slice)
odd := filter(slice, isOdd) // usamos la función como un valor
fmt.Println("Odd elements of slice are: ", odd)
even := filter(slice, isEven)
fmt.Println("Even elements of slice are: ", even)
}
```
Es muy útil cuando usamos interfaces. Como puedes ver `testInt` es una variable que tiene como tipo una función, y devuelve valores y el argumento de `filter` es el mismo que el de `testInt`. Por lo tanto, tenemos una lógica mas compleja en nuestro programa, y hacemos nuestro código mas flexible.
### Panic y Recover
Go no tiene estructura `try-catch` como lo tiene Java. En vez de lanzar excepciones, Go usa `panic` y `recover` para hacer frente a los errores. Sin embargo, no debería usar mucho `panic`, aunque sea muy poderoso.
Panic es una función incorporada para romper el flujo normal del programa y entrar en un estado de pánico. Cuando la función `F` llama a `panic`, la función `F` no continuara ejecutándose, pero sus funciones `defer` siempre se ejecutaran. Entonces `F` vuelve a su punto de ruptura donde se causo el estado de pánico. El programa no va a finalizar hasta que todas las funciones retornen con `panic` hasta el primer nivel de esa `goroutine`. `panic` se puede producir con una llamada a `panic` en el programa, y algunos errores pueden causar una llamada a `panic` como un intento de acceso fuera de un array.
Recover es una función incorporada para recuperar una `goroutine` de un estado de pánico, solo el llamado a `recover` es útil en una función `defer` porque las funciones normales no van a ser ejecutadas cuando el programa se encuentre en un estado de pánico. Vamos a poder atrapar el valor de `panic` si el programa se encuentra en un estado de pánico, este nos va a devolver `nil` si el programa se encuentra en un estado normal.
El siguiente ejemplo nos muestra como utilizar `panic`.
```
var user = os.Getenv("USER")
func init() {
if user == "" {
panic("no hay valor para $USER")
}
}
```
El siguiente ejemplo nos muestra como verificar un `panic`.
```
func throwsPanic(f func()) (b bool) {
defer func() {
if x := recover(); x != nil {
b = true
}
}()
f() // si f causa un panic, este va a recuperase (recover)
return
}
```
### La función `main` y la función `init`
Go tiene dos retenciones (retention) que son llamadas `main` e `init`, donde `init` puede ser usada en todos los paquetes y `main` solo puede ser usada en el paquete `main`. Estas dos funciones no son capaces de tener argumentos o valores de retorno. A pesar de que podemos escribir muchas funciones `init` en un mismo paquete, yo recomiendo fuertemente que escriban solo una función `init` por cada paquete.
Los programas en Go van a llamar a `init()` y a `main()` automáticamente, así que no es necesario llamarlas. Para cada paquete, la función `init` es opcional, pero `package main` tiene una y solo una función `main`.
Los programas se inicializan y se ejecutan desde el paquete `main`, si el paquete `main` importa otros paquetes, ellos serán importados en tiempo de compilación. Si un paquete es importado muchas veces, este va a ser compilado solo una vez. Después de importar los paquetes, el programa va a inicializar las constantes y variables en los paquetes importados, luego va a ejecutar la función `init` si es que existe, y así sucesivamente. Después de que todos los paquetes fueran inicializados, el programa va a comenzar a inicializar las constantes y variables en el paquete `main`, entonces va a ejecutar la función `init` en el paquete si es que existe. La siguiente figura les va a mostrar el proceso.
![](images/2.3.init.png?raw=true)
Figure 2.6 Flujo de inicialización de un programa en Go
### import
Nosotros usamos `import` muy frecuentemente en los programas en Go como se muestra acá.
```
import(
"fmt"
)
```
Entonces nosotros podemos usar las funciones de ese paquete de la siguiente forma.
fmt.Println("Hola mundo")
`fmt` es parte de la librería estándar de Go , esta localizado en $GOROOT/pkg. Go utiliza las siguiente dos formas para paquete de terceros.
1. Path relativo
import "./model" // carga el paquete que se encuentra en el mismo directorio, yo no recomiendo utilizar esta forma.
2. Path absoluto
import "shorturl/model" // carga un paquete en el path "$GOPATH/pkg/shorturl/model"
Tenemos algunos operadores especiales para importar paquetes, y los principiantes normalmente se confunden con estos operadores.
1. EL operador punto .
A veces podemos ver personas que utilizan la siguiente forma para importar paquetes.
import(
. "fmt"
)
El operador punto significa que podemos omitir el nombre del paquete cuando llamamos a las funciones del mismo. En vez de esto `fmt.Printf("Hola mundo")` ahora podemos usar esto `Printf("Hola mundo")`.
2. El operador alias.
Este puede cambiar el nombre del paquete que vamos a importar cuando llamamos a las funciones del mismo.
import(
f "fmt"
)
En vez de esto `fmt.Printf("Hola mundo")` ahora podemos usar esto `f.Printf("Hola mundo")`.
3. El operador `_`.
Este es un operador un poco difícil de comprender si no tenemos una breve explicación.
import (
"database/sql"
_ "github.com/ziutek/mymysql/godrv"
)
El operador `_` en realidad significa que solo importamos ese paquete, y usamos la función `init` de ese paquete, y no estamos seguros si vamos a utilizar funciones de dicho paquete.
## Enlaces
- [Índice](preface.md)
- Sección anterior: [Principios de Go](02.2.md)
- Siguiente sección: [struct](02.4.md)

214
es/02.4.md Normal file
View File

@@ -0,0 +1,214 @@
# 2.4 struct
## struct
Podemos definir en Go nuevos tipos de contenedores con otras propiedades o campos como en otros lenguajes de programación. Por ejemplo, podemos crear el tipo llamado `persona` para representar una persona, este tipo tiene nombre y edad. Podemos llamar estos tipos de tipos como `struct`.
```
type persona struct {
nombre string
edad int
}
```
Mira que fácil es definir un `struct`!
Tiene dos campos.
- `nombre` es una `string` usada para guardar el nombre de personas.
- `edad` es un `int` usado para guardar la de edad de personas.
Vamos a ver como usarlo.
```
type persona struct {
nombre string
edad int
}
```
var P persona // p es de tipo persona
P.nombre = "Astaxie" // asigna "Astaxie" al campo 'nombre' de p
P.edad = 25 // asigna 25 al campo 'edad' de p
fmt.Printf("El nombre de la persona es %s\n", P.name) // accedemos al campo 'nombre' de p
Tenemos tres formas mas de definir un struct.
- Asignando un valor inicial en forma ordenada
P := persona{"Tom", 25}
- Usando el formato `campo:valor` para inicializarlo sin orden
P := persona{edad:24, nombre:"Bob"}
- Definimos una struct anónima, y la inicializamos
P := struct{nombre string; edad int}{"Amy",18}
Vamos a ver un ejemplo completo.
```
package main
import "fmt"
// definimos un tipo nuevo
type persona struct {
nombre string
edad int
}
// comparamos la edad de dos personas, y devolvemos la mas vieja con la
// diferencia, struct es pasado por valor
func Older(p1, p2 persona) (persona, int) {
if p1.edad>p2.edad {
return p1, p1.edad-p2.edad
}
return p2, p2.edad-p1.edad
}
func main() {
var tom persona
// inicialización
tom.nombre, tom.edad = "Tom", 18
// inicializamos los dos valores con el formato "campo:valor"
bob := persona{edad:25, nombre:"Bob"}
// inicializamos los dos valores en orden
paul := persona{"Paul", 43}
tb_Older, tb_diff := Older(tom, bob)
tp_Older, tp_diff := Older(tom, paul)
bp_Older, bp_diff := Older(bob, paul)
fmt.Printf("De %s y %s, %s es mas viejo por %d años\n", tom.nombre, bob.nombre , tb_Older.nombre , tb_diff)
fmt.Printf("De %s y %s, %s es mas viejo por %d años\n", tom.nombre, paul.nombre, tp_Older.nombre, tp_diff)
fmt.Printf("De %s y %s, %s es mas viejo por %d años\n", bob.nombre, paul.nombre, bp_Older.nombre, bp_diff)
}
```
### Campos incrustados en un struct
Solo les mostré como definir struct con campos que tienen nombre y tipo. De hecho, Go soporta campos sin nombre pero si con tipo, vamos a llamar a estos campos incrustados.
Cuando el campo incrustado es un struct, todos los campos de ese struct serán campos del nuevo struct de forma implícita.
Vamos a ver un ejemplo.
```
package main
import "fmt"
type Human struct {
name string
age int
weight int
}
type Student struct {
Human // campo incrustado, esto significa que el struct Student va a incluir los campos que tiene Human.
speciality string
}
func main() {
// inicializamos a student
mark := Student{Human{"Mark", 25, 120}, "Computer Science"}
// campos accesibles
fmt.Println("Su nombre es ", mark.name)
fmt.Println("Su edad es ", mark.age)
fmt.Println("Su peso es ", mark.weight)
fmt.Println("Su especialidad es ", mark.speciality)
// modificamos un campo
mark.speciality = "AI"
fmt.Println("Mark cambio su especialidad")
fmt.Println("Su especialidad es ", mark.speciality)
// modificamos su edad
fmt.Println("Mark esta mas viejo")
mark.age = 46
fmt.Println("Su edad es ", mark.age)
// modificamos su peso
fmt.Println("Mark ya no es mas un atleta")
mark.weight += 60
fmt.Println("Su peso es ", mark.weight)
}
```
![](images/2.4.student_struct.png?raw=true)
Figure 2.7 Herencia en Student y Human
Vemos que accedemos a la edad y nombre en Student de la misma forma que lo hacemos con Human. Así es como funcionan los campos incrustados. Es muy útil “cool”, no lo es? Espera, hay algo todavía mas “cool”! Puede utilizar a Student para acceder a los campos incrustados de Human!
mark.Human = Human{"Marcus", 55, 220}
mark.Human.age -= 1
Todos los tipos pueden ser utilizados como campos incrustados.
```
package main
import "fmt"
type Skills []string
type Human struct {
name string
age int
weight int
}
type Student struct {
Human // struct como un campo incrustado
Skills // string como un campo incrustado
int // usamos un tipo embebido como un campo incrustado
speciality string
}
func main() {
// inicializamos al Student Jane
jane := Student{Human:Human{"Jane", 35, 100}, speciality:"Biology"}
// accedemos a sus campos
fmt.Println("Su nombre es ", jane.name)
fmt.Println("Su edad es , jane.age)
fmt.Println("Su peso es ", jane.weight)
fmt.Println("Su especialidad es ", jane.speciality)
// modificamos el valor del campo skill
jane.Skills = []string{"anatomy"}
fmt.Println("Sus habilidades son ", jane.Skills)
fmt.Println("Ella adquirió don habilidades mas ")
jane.Skills = append(jane.Skills, "physics", "golang")
fmt.Println("Sus habilidades ahora son ", jane.Skills)
// modificamos un campo embebido
jane.int = 3
fmt.Println("Su numero preferido es ", jane.int)
}
```
En el ejemplo anterior, podemos ver que a todos los tipos se les pueden incrustar campos y podemos utilizar funciones para operarlos.
Hay un problema mas, si Human tiene un campo llamado `phone` y Student tiene otro campo llamado con el mismo nombre, que deberíamos hacer?
Go utiliza una forma muy sencilla para resolverlo. Los campos exteriores consiguen accesos superiores, lo que significa, es que cuando se accede a `student.phone`, obtendremos el campo llamado phone en student, no en el struct de Human. Esta característica se puede ver simplemente como una `sobrecarga` de campos.
```
package main
import "fmt"
type Human struct {
name string
age int
phone string // Human tiene el campo phone
}
type Employee struct {
Human // campo embebido Human
speciality string
phone string // phone en employee
}
func main() {
Bob := Employee{Human{"Bob", 34, "777-444-XXXX"}, "Designer", "333-222"}
fmt.Println("El teléfono del trabajo de Bob es:", Bob.phone)
// accedemos al campo phone en Human
fmt.Println("El teléfono personal de Bob es:", Bob.Human.phone)
}
```
## Enlaces
- [Índice](preface.md)
- Sección anterior: [Sentencias de control y funciones](02.3.md)
- Siguiente sección: [Orientado a objetos](02.5.md)

307
es/02.5.md Normal file
View File

@@ -0,0 +1,307 @@
# Orientado a objetos
En las ultimas dos secciones hablamos de funciones y struct, alguna vez pensaste en usar funciones como campos de un struct? En esta sección, voy a realizar una introducción a otro tipo de funciones , que vamos a llamar métodos (`method`).
## Métodos
Supongamos que definimos un struct del estilo rectángulo(rectangle), y buscas calcular el área, por lo general vamos a usar el siguiente código para lograr este objetivo.
```
package main
import "fmt"
type Rectangle struct {
width, height float64
}
func area(r Rectangle) float64 {
return r.width*r.height
}
func main() {
r1 := Rectangle{12, 2}
r2 := Rectangle{9, 4}
fmt.Println("El área de r1 es: ", area(r1))
fmt.Println("El área de r2 is: ", area(r2))
}
```
En el ejemplo de arriba podemos calcular el área de un rectángulo, usamos la función llamada `area`, pero no es un método de un struct “rectangle” (como un método en un clase en un lenguaje orientado a objeto clásico). La función y el struct como se ve son dos cosas independientes.
Hasta ahora eso no es un problema. Pero que pasa si quiere calcular el área de un circulo, cuadrado, pentágono, u otra figura, vamos a tener que agregar mas funciones con nombres muy similares.
![](images/2.5.rect_func_without_receiver.png?raw=true)
Figure 2.8 Relación entre funciones y struct
Obviamente, no es “cool”. Además el área debería ser propiedad del circulo o el rectángulo.
Por estas razones, tenemos conceptos sobre `métodos`. `métodos` es afiliado a un tipo, tienen la misma sintaxis que una función excepto por una palabra más después de la palabra reservada `func` que es llamada receptor (`receiver`) que es el cuerpo principal de ese método.
Utiliza el mismo ejemplo, `Rectangle.area()` pertenece al rectángulo, no como una función periférica. Mas específicamente, `length`, `width` y `area()` todos pertenecen a rectángulo.
Como dice Rob Pike.
"Un método es una función con un primer argumento implícito, llamado receiver."
Sintaxis de un método.
```
func (r ReceiverType) funcName(parameters) (results)
```
Cambiemos el ejemplo anterior para utilizar métodos.
```
package main
import (
"fmt"
"math"
)
type Rectangle struct {
width, height float64
}
type Circle struct {
radius float64
}
func (r Rectangle) area() float64 {
return r.width*r.height
}
func (c Circle) area() float64 {
return c.radius * c.radius * math.Pi
}
func main() {
r1 := Rectangle{12, 2}
r2 := Rectangle{9, 4}
c1 := Circle{10}
c2 := Circle{25}
fmt.Println("El área de r1 es: ", r1.area())
fmt.Println("El área de r2 es: ", r2.area())
fmt.Println("El área de c1 es: ", c1.area())
fmt.Println("El área de c2 es: ", c2.area())
}
```
Notas para el uso de métodos.
- Si el nombre de un método es el mismo, pero no tiene el mismo receptor, ellos no son iguales.
- Los métodos son capaces de acceder a los campos en los receptores.
- Usamos el `.` para llamar al método en el struct, al igual que cuando llamamos a un campo.
![](images/2.5.shapes_func_with_receiver_cp.png?raw=true)
Figure 2.9 Los métodos son diferentes en diferentes struct
En el ejemplo anterior, el método area() es respectivamente perteneciente a Rectangle y a Circle, por lo que los receptores son Rectangle y Circle.
Una cosa es importante notar, que un método con una línea de puntos significa que el receptor se pasa por valor, no por referencia. La diferencia entre ellos es que el método podría cambiar el valor del receptor cuando el mismo es pasado por referencia, y este toma una copia del receptor cuando es pasado por valor.
¿El receptor puede ser unicamente un struct? Por supuesto que no, cualquier tipo podría ser el receptor en un método. Puede ser un poco confuso cuando hablamos de tipos personalizados(“customized type”), struct es un tipo especial de tipo modificado, hay varios tipos personalizado.
Utilice el siguiente formato para definir un tipo personalizado.
```
type typeName typeLiteral
```
Un ejemplo sobre tipos personalizados.
```
type ages int
type money float32
type months map[string]int
m := months {
"January":31,
"February":28,
...
"December":31,
}
```
Espero que ahora sepa como utilizar los tipos personalizados. Es similar a `typedef` en C, nosotros usamos `ages` para sustituir a `int` en el ejemplo anterior.
Volvamos a los `métodos`.
Puedes utilizar tantos métodos en tipos personalizados como desees.
```
package main
import "fmt"
const(
WHITE = iota
BLACK
BLUE
RED
YELLOW
)
type Color byte
type Box struct {
width, height, depth float64
color Color
}
type BoxList []Box //un slice de boxes
func (b Box) Volume() float64 {
return b.width * b.height * b.depth
}
func (b *Box) SetColor(c Color) {
b.color = c
}
func (bl BoxList) BiggestsColor() Color {
v := 0.00
k := Color(WHITE)
for _, b := range bl {
if b.Volume() > v {
v = b.Volume()
k = b.color
}
}
return k
}
func (bl BoxList) PaintItBlack() {
for i, _ := range bl {
bl[i].SetColor(BLACK)
}
}
func (c Color) String() string {
strings := []string {"WHITE", "BLACK", "BLUE", "RED", "YELLOW"}
return strings[c]
}
func main() {
boxes := BoxList {
Box{4, 4, 4, RED},
Box{10, 10, 1, YELLOW},
Box{1, 1, 20, BLACK},
Box{10, 10, 1, BLUE},
Box{10, 30, 1, WHITE},
Box{20, 20, 20, YELLOW},
}
fmt.Printf("Tenemos %d boxes en nuestro conjunto\n", len(boxes))
fmt.Println("El volumen de la primera es ", boxes[0].Volume(), "cm³")
fmt.Println("El color de la última es",boxes[len(boxes)-1].color.String())
fmt.Println("La más grande es", boxes.BiggestsColor().String())
fmt.Println("Vamos a pintarlas a todas de negro")
boxes.PaintItBlack()
fmt.Println("El color de la segunda es", boxes[1].color.String())
fmt.Println("Obviamente, ahora, la más grande es", boxes.BiggestsColor().String())
}
```
Vamos a definir algunas constantes y tipos personalizados.
- Usamos `Color` como un alias de `byte`.
- Definimos un struct `Box` que tiene los campos height, width, length y color.
- Definimos un struct `BoxList` que tiene `Box` como sus campos.
Entonces vamos a definir algunos métodos para personalizar nuestros tipos.
- Volume() usa a Box como su receptor, devuelve el volumen de Box.
- SetColor(c Color) cambiar el color de las Box.
- BiggestsColor() devuelve el colo que tiene la de mayor volumen.
- PaintItBlack() configuramos el color para todas las Box en BoxList a negro.
- String() usamos Color como su receptor, devolvemos un string con formato de el nombre del color.
¿Es mucho mas claro cuando usamos palabras para describir nuestro requerimientos? Usualmente escribimos nuestros requerimientos antes de comenzar a programar.
### Utilizar punteros como un receptor
Vamos a mirar el método `SetColor`, su receptor es un puntero de tipo Box. Si, puedes usar `*Box` como receptor. ¿Por qué usaríamos un puntero aquí? Porque buscamos cambiar el color de Box en este método, si no usamos un puntero, solo cambiaría el valor de la copia de Box.
Si vemos el receptor como el primer argumento de los métodos, no es difícil de entender como funcionan estos.
Podría decir que deberíamos usar `*b.Color=c` en vez de `b.Color=c` en el método SetColor(). Cualquiera de los dos estaría bien, porque Go lo sabe interpretar. ¿Ahora crees que Go es mas fascinante?
También podría decir que deberíamos usar `(&bl[i]).SetColor(BLACK)` en `PaintItBlack` porque le pasamos un puntero a `SetColor`. Una vez más, cualquiera de los dos esta bien, porque ¡Go sabe interpretarlo correctamente!
### Herencia de métodos
Aprendimos sobre la herencia de campos en la última sección, también tenemos la herencia de métodos en Go. Así que si un campo anónimo tiene métodos, el struct que contiene el campo tendrá todos los métodos del mismo.
```
package main
import "fmt"
type Human struct {
name string
age int
phone string
}
type Student struct {
Human // campo anónimo
school string
}
type Employee struct {
Human
company string
}
// definimos un método en Human
func (h *Human) SayHi() {
fmt.Printf("Hola, Yo soy %s puedes llamarme al %s\n", h.name, h.phone)
}
func main() {
mark := Student{Human{"Mark", 25, "222-222-YYYY"}, "MIT"}
sam := Employee{Human{"Sam", 45, "111-888-XXXX"}, "Golang Inc"}
mark.SayHi()
sam.SayHi()
}
```
### Sobrecarga de métodos
Si queremos que Employee tenga su propio método llamado `SayHi`, podemos definir el método con el mismo nombre que el de Employee, y así ocultaremos `SayHi` de Human cuando lo llamemos.
```
package main
import "fmt"
type Human struct {
name string
age int
phone string
}
type Student struct {
Human
school string
}
type Employee struct {
Human
company string
}
func (h *Human) SayHi() {
fmt.Printf("Hola, Yo soy %s podes llamarme al %s\n", h.name, h.phone)
}
func (e *Employee) SayHi() {
fmt.Printf("Hola, Yo soy %s, trabajo en %s. Podes llamarme al %s\n", e.name,
e.company, e.phone) //Si, lo podes cortar en dos líneas.
}
func main() {
mark := Student{Human{"Mark", 25, "222-222-YYYY"}, "MIT"}
sam := Employee{Human{"Sam", 45, "111-888-XXXX"}, "Golang Inc"}
mark.SayHi()
sam.SayHi()
}
```
Ahora eres capaz de escribir programas que utilicen el paradigma Orientado a Objetos, los métodos utilizan la regla de que los que tienen nombre que se inician con mayúsculas van a ser públicos o privados en caso contrario.
## Enlaces
- [Índice](preface.md)
- Sección anterior: [struct](02.4.md)
- Siguiente sección: [interfaces](02.6.md)

395
es/02.6.md Normal file
View File

@@ -0,0 +1,395 @@
# 2.6 Interfaces
## Interfaces
Una de las características de diseño mas sutiles en Go son las interfaces. Después de leer esta sección, probablemente quedara impresionado por su implementación.
### Que es una interfaz?
En pocas palabras, una interfaz es un conjunto de métodos, que se usan para definir un conjunto de acciones.
Al igual que en los ejemplos de las secciones anteriores, ambos, Student y Employee pueden llamar a `SayHi()`, pero ellos no hacen lo mismo.
Vamos a trabajar un poco mas, vamos a agregarle a ellos el método `Sing()`, y también vamos a agregar `BorrowMoney()` a Student y `SpendSalary()` a Employee.
Ahora Student tiene tres métodos llamados `SayHi()`, `Sing()`, `BorrowMoney()`, y Employee tiene `SayHi()`, `Sing()` y `SpendSalary()`.
Esta combinación de métodos es llamada interfaz, y es implementada por Student y Employee. Entonces Student y Employee implementan la interfaz: `SayHi()`, `Sing()`. Al mismo tiempo, Employee no implementa la interfaz: `SayHi()`, `Sing()`, `BorrowMoney()`, y Student no implementa la interfaz: `SayHi()`, `Sing()`, `SpendSalary()`. Esto es porque Employee no tiene el método `BorrowMoney()` y Student no tiene el método `SpendSalary()`.
### Tipo de Interfaces
Una interfaz define un grupo de métodos, entonces si un tipo implementa todos los métodos entonces nosotros decimos que ese tipo implementa la interfaz.
```
type Human struct {
name string
age int
phone string
}
type Student struct {
Human
school string
loan float32
}
type Employee struct {
Human
company string
money float32
}
func (h *Human) SayHi() {
fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
}
func (h *Human) Sing(lyrics string) {
fmt.Println("La la, la la la, la la la la la...", lyrics)
}
func (h *Human) Guzzle(beerStein string) {
fmt.Println("Guzzle Guzzle Guzzle...", beerStein)
}
// Employee sobrescribe Sayhi
func (e *Employee) SayHi() {
fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name,
e.company, e.phone) //Si acá podes cortarlo en 2 líneas.
}
func (s *Student) BorrowMoney(amount float32) {
s.loan += amount // (otra vez...)
}
func (e *Employee) SpendSalary(amount float32) {
e.money -= amount
}
// definimos algunas interfaces
type Men interface {
SayHi()
Sing(lyrics string)
Guzzle(beerStein string)
}
type YoungChap interface {
SayHi()
Sing(song string)
BorrowMoney(amount float32)
}
type ElderlyGent interface {
SayHi()
Sing(song string)
SpendSalary(amount float32)
}
```
Sabemos que una interfaz puede ser implementada por cualquier tipo, y un tipo puede implementar muchas interfaces al mismo tiempo.
Tenga en cuenta que todos los tipos implementan la interfaz vacía `interface{}` porque esta no tiene ningún método y todos los tipos tienen cero métodos por defecto.
### Valor de una interfaz
Entonces, que tipo de valores se puede poner en una interfaz? Si definimos una variable como tipo interfaz, cualquier tipo que implemente la interfaz puede asignarse a esta variable.
Al igual que en el ejemplo anterior, si definimos una variable m como la interfaz Men, entonces cualquier Student, Human o Employee puede ser asignado a m. Entonces podríamos tener una lista de Men, y cualquier tipo que implemente la interfaz Men puede agregarse a esa lista (slice). Sin embargo sea consciente que una lista de interfaces no tiene el mismo comportamiento que una lista de otros tipos.
```
package main
import "fmt"
type Human struct {
name string
age int
phone string
}
type Student struct {
Human
school string
loan float32
}
type Employee struct {
Human
company string
money float32
}
func (h Human) SayHi() {
fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
}
func (h Human) Sing(lyrics string) {
fmt.Println("La la la la...", lyrics)
}
func (e Employee) SayHi() {
fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name,
e.company, e.phone) //Si acá podes cortarlo en 2 líneas.
}
// La interfaz Men es implementada por Human, Student y Employee
type Men interface {
SayHi()
Sing(lyrics string)
}
func main() {
mike := Student{Human{"Mike", 25, "222-222-XXX"}, "MIT", 0.00}
paul := Student{Human{"Paul", 26, "111-222-XXX"}, "Harvard", 100}
sam := Employee{Human{"Sam", 36, "444-222-XXX"}, "Golang Inc.", 1000}
Tom := Employee{Human{"Sam", 36, "444-222-XXX"}, "Things Ltd.", 5000}
// definimos la interfaz i
var i Men
//i podemos guardar un Student
i = mike
fmt.Println("This is Mike, a Student:")
i.SayHi()
i.Sing("November rain")
//i podemos guardar un Employee
i = Tom
fmt.Println("This is Tom, an Employee:")
i.SayHi()
i.Sing("Born to be wild")
// lista de Men
fmt.Println("Let's use a slice of Men and see what happens")
x := make([]Men, 3)
// estos tres elementos son de diferentes tipos pero todos ellos implementan la interfaz Men
x[0], x[1], x[2] = paul, sam, mike
for _, value := range x {
value.SayHi()
}
}
```
Una interfaz es un conjunto de métodos abstractos, y puede ser implementada por tipos que no son interfaces. Por lo tanto esta no puede implementarse a si misma.
### Interfaz vacía
Una interfaz vacía es una interfaz que no contiene ningún método, entonces todos los tipos implementan una interfaz vacía. Es muy útil cuando buscamos guardar todos los tipos en el mismo lugar, y es similar a void* en C.
```
// definimos una interfaz vacía
var a interface{}
var i int = 5
s := "Hello world"
// a puede guardar un valor de cualquier tipo
a = i
a = s
```
Si una función usa una interfaz vacía como su tipo de argumento, esta puede aceptar cualquier tipo; si una función usa una interfaz vacía como el tipo de valor de retorno, esta puede devolver cualquier tipo.
### Argumentos de métodos de una interfaz
Cualquier variable puede usarse en una interfaz, entonces podemos pensar sobre como podemos usar esta característica para pasar cualquier tipo de variable a una función.
Por ejemplo, usamos mucho fmt.Println, pero alguna vez notaste que ¿acepta cualquier tipo de argumento? Si vemos el código fuente de fmt que es libre, podemos ver la siguiente definición.
```
type Stringer interface {
String() string
}
```
Esto significa que cualquier tipo que implemente la interfaz Stringer puede ser pasada a fmt.Println como argumento. Vamos a probarlo.
```
package main
import (
"fmt"
"strconv"
)
type Human struct {
name string
age int
phone string
}
// Human implementa fmt.Stringer
func (h Human) String() string {
return "Name:" + h.name + ", Age:" + strconv.Itoa(h.age) + " years, Contact:" + h.phone
}
func main() {
Bob := Human{"Bob", 39, "000-7777-XXX"}
fmt.Println("This Human is : ", Bob)
}
```
Volviendo atrás al ejemplo de Box, podemos ver que Color también implementa la interfaz Stringer, por lo que somos capaces de personalizar el formato de impresión. Si no implementamos esa interfaz, fmt.Println imprimirá el tipo con el formato por defecto.
```
fmt.Println("The biggest one is", boxes.BiggestsColor().String())
fmt.Println("The biggest one is", boxes.BiggestsColor())
```
Atención: Si el tipo implementa la interfaz `error`, fmt va a llamar a `error()`, entonces en este punto no tendrás que implementar Stringer.
### Tipos de variables en un interfaz
Si una variable es del tipo que implementa una interfaz, sabemos que cualquier otro tipo que implemente la misma interfaz puede ser asignada a esta variable. La pregunta es ¿Cómo podemos saber cual es el tipo específico almacenado en la interfaz? Tenemos dos formas, que te voy a comentar a continuación.
- Patron de Aserción Comma-ok
Go tiene la sintaxis `value, ok := element.(T)`. Esto comprueba si la variable es del tipo que se espero, donde value es el valor de la variable, y ok es un valor de tipo booleano, element es la variable interfaz y T es el tipo que se afirma tener.
Si el elemento es del tipo que esperamos, ok será true, en otro caso será false.
Veamos un ejemplo para verlo con más claridad.
```
package main
import (
"fmt"
"strconv"
)
type Element interface{}
type List []Element
type Person struct {
name string
age int
}
func (p Person) String() string {
return "(Nombre: " + p.name + " - edad: " + strconv.Itoa(p.age) + " años)"
}
func main() {
list := make(List, 3)
list[0] = 1 // un int
list[1] = "Hola" // una string
list[2] = Person{"Dennis", 70}
for index, element := range list {
if value, ok := element.(int); ok {
fmt.Printf("list[%d] is an int and its value is %d\n", index, value)
} else if value, ok := element.(string); ok {
fmt.Printf("list[%d] is a string and its value is %s\n", index, value)
} else if value, ok := element.(Person); ok {
fmt.Printf("list[%d] is a Person and its value is %s\n", index, value)
} else {
fmt.Println("list[%d] is of a different type", index)
}
}
}
```
Es bastante sencillo usar este patrón, pero si tenemos que verificar varios tipos, es mejor que usemos un `switch`.
- Verificación con un switch
Vamos a ver el uso de `switch` para reescribir el ejemplo anterior.
```
package main
import (
"fmt"
"strconv"
)
type Element interface{}
type List []Element
type Person struct {
name string
age int
}
func (p Person) String() string {
return "(Nombre: " + p.name + " - edad: " + strconv.Itoa(p.age) + " años)"
}
func main() {
list := make(List, 3)
list[0] = 1 //un int
list[1] = "Hello" //una string
list[2] = Person{"Dennis", 70}
for index, element := range list {
switch value := element.(type) {
case int:
fmt.Printf("list[%d] is an int and its value is %d\n", index, value)
case string:
fmt.Printf("list[%d] is a string and its value is %s\n", index, value)
case Person:
fmt.Printf("list[%d] is a Person and its value is %s\n", index, value)
default:
fmt.Println("list[%d] is of a different type", index)
}
}
}
```
Una cosa que deberíamos recordar es que `element.(type)` no puede ser usado fuera del cuerpo del `switch`, lo que significa que en este caso no puedes usar el patrón `comma-ok`.
### Interfaces embebidas
La cosa más atractiva es que Go tiene mucha lógica embebida en su sintaxis, como campos anónimos en un struct. No es para sorprenderse, que podamos usar interfaces también como campos anónimos, pero vamos a llamarlas `Interfaces embebidas`. Aquí, vamos a seguir las mismas reglas que para los campos anónimos. Más específicamente, si una interfaz tiene otra interfaz como una interfaz embebida, esta tendrá todos los métodos que la clase embebida tiene.
Podemos ver el archivo fuente `container/heap` que tiene una definición como la siguiente.
```
type Interface interface {
sort.Interface // embebida sort.Interface
Push(x interface{}) //el método Push para empujar elementos a la pila
Pop() interface{} //el elemento Pop que saca elementos de la pila
}
```
Podemos ver que `sort.Interface` es una interfaz embebida, por lo que la interfaz anterior tiene tres métodos que son explícitos de `sort.Interface`.
```
type Interface interface {
// Len es el número de elementos en la colección.
Len() int
// Less devuelve si el elemento de índice i se debe ordenar
// antes que el elemento con índice j.
Less(i, j int) bool
// Swap intercambia los elementos con índices i y j.
Swap(i, j int)
}
```
Otro ejemplo es el de `io.ReadWriter` en el paquete `io`.
```
// io.ReadWriter
type ReadWriter interface {
Reader
Writer
}
```
### Reflexión
La reflexión en Go es usada para obtener información en tiempo de ejecución. Vamos a usar el paquete `reflect`, y este articulo oficial [articulo](http://golang.org/doc/articles/laws_of_reflection.html) explica como funciona la reflexión en Go.
Tenemos que realizar tres pasos para usar reflect. Primero, necesitamos convertir una interfaz en un tipo reflect types (reflect.Type o reflect.Value, depende en que situación nos encontremos).
```
t := reflect.TypeOf(i) // tomamos la meta-data de el tipo i, y usamos t para tomar todos los elementos
v := reflect.ValueOf(i) // tomamos el valor actual de el tipo i, y usamos v para cambiar este valor
```
Después de eso, necesitamos convertir el tipo reflect de el valor que tomamos a el tipo que necesitamos.
```
var x float64 = 3.4
v := reflect.ValueOf(x)
fmt.Println("type:", v.Type())
fmt.Println("kind is float64:", v.Kind() == reflect.Float64)
fmt.Println("value:", v.Float())
```
Finalmente, si buscamos cambiar el valor que vino del tipo reflects, necesitamos hacerlo modificable. Como hablamos antes, esta es una diferencia entre pasar por valor o por referencia. El siguiente código no compilará.
```
var x float64 = 3.4
v := reflect.ValueOf(x)
v.SetFloat(7.1)
```
En lugar de eso, debemos usar el siguiente código para cambiar el valor de los tipos reflect.
```
var x float64 = 3.4
p := reflect.ValueOf(&x)
v := p.Elem()
v.SetFloat(7.1)
```
Acabamos de hablar sobre los conocimientos básicos de sobre el uso de reflexión (reflection), deberías practicar más para entenderlo mejor.
## Enlaces
- [Índice](preface.md)
- Sección anterior: [Orientado a objetos](02.5.md)
- Siguiente sección: [Concurrencia](02.7.md)

242
es/02.7.md Normal file
View File

@@ -0,0 +1,242 @@
# Concurrencia
Se dice que Go es el lenguaje C del siglo 21. Pienso que hay dos razones para eso: la primera, Go es un lenguaje simple; segundo, la concurrencia es un tema candente en el mundo de hoy, y Go soporta esta característica a nivel de lenguaje.
## goroutine
goroutines y concurrencia están integradas en el diseño del núcleo de Go. Ellas son similares a los hilos pero trabajan de forma diferente. Más de una docena de goroutines a lo mejor por debajo solo tienen 5 o 6 hilos. Go también nos da soporte completo para compartir memoria entre sus goroutines. Una goroutine usualmente usa 4~5 KB de memoria en la pila. Por lo tanto, no es difícil ejecutar miles de goroutines en una sola computadora. Una goroutine es mas liviana, más eficiente, y más conveniente que los hilos del sistema.
Las goroutines corren en el administrador de procesos en tiempo de ejecución en Go. Usamos la palabra reservada `go` para crear una nueva goroutine, que por debajo es una función ( ***main() es una goroutine*** ).
```
go hello(a, b, c)
```
Vamos a ver un ejemplo.
```
package main
import (
"fmt"
"runtime"
)
func say(s string) {
for i := 0; i < 5; i++ {
runtime.Gosched()
fmt.Println(s)
}
}
func main() {
go say("world") // creamos una nueva goroutine
say("hello") // actual goroutine
}
```
Salida
```
hello
world
hello
world
hello
world
hello
world
hello
```
Podemos ver que es muy fácil usar concurrencia en Go usando la palabra reservada `go`. En el ejemplo anterior, estas dos goroutines comparten algo de memoria, pero sería mejor si utilizáramos la receta de diseño: No utilice datos compartidos para comunicarse, use comunicación para compartir datos.
runtime.Gosched() le dice al CPU que ejecute otras goroutines, y que en algún punto vuelva.
El manejador de tareas solo usa un hilo para correr todas la goroutines, lo que significa que solo implementa la concurrencia. Si buscas utilizar mas núcleos del CPU para usar mas procesos en paralelo, tenemos que llamar a runtime.GOMAXPROCS(n) para configurar el numero de núcleos que deseamos usar. Si `n<1`, esto no va a cambiar nada. Esta función se puede quitar en el futuro, para ver mas detalles sobre el procesamiento en paralelo y la concurrencia vea el siguiente [articulo](http://concur.rspace.googlecode.com/hg/talk/concur.html#landing-slide).
## Canales
goroutines son ejecutadas en el mismo espacio de direcciones de memoria, por lo que se tiene que mantener sincronizadas si buscas acceder a la memoria compartida. ¿Cómo nos comunicamos entre diferentes goroutines? Go utiliza un muy buen mecanismo de comunicación llamado `canales`(channel). Los `canales` son como dos tuberías (o pipes) en la shell de Unix: usando canales para enviar o recibir los datos. El unico tipo de datos que se puede usar en los canales es el tipo `channel` y la palabra reservada para eso es `chan`. Ten en cuenta que para crear un nuevo `channel` debemos usar la palabra reservada `make`.
```
ci := make(chan int)
cs := make(chan string)
cf := make(chan interface{})
```
Los canales usan el operador `<-` para enviar o recibir datos.
```
ch <- v // enviamos v al canal ch.
v := <-ch // recibimos datos de ch, y lo asignamos a v
```
Vemos esto en un ejemplo.
```
package main
import "fmt"
func sum(a []int, c chan int) {
total := 0
for _, v := range a {
total += v
}
c <- total // enviamos total a c
}
func main() {
a := []int{7, 2, 8, -9, 4, 0}
c := make(chan int)
go sum(a[:len(a)/2], c)
go sum(a[len(a)/2:], c)
x, y := <-c, <-c // recibimos de c
fmt.Println(x, y, x + y)
}
```
Enviando y recibimos los datos por defecto en bloques, por lo que es mucho mas fácil usar goroutines sincrónicas. Lo que quiero decir, es que el bloque en la goroutine no va a continuar cuando reciba datos de un canal vacío (`value := <-ch`), hasta que otras goroutines envíen datos a este canal. Por otro lado, la goroutine por otro lado no enviara datos al canal (`ch<-5`) hasta que no reciba datos.
## Buffered channels
Anteriormente hice una introducción sobre canales non-buffered channels (non-buffered channels), y Go también tiene 'buffered channels' que pueden guardar mas de un elemento. Por ejemplo, `ch := make(chan bool, 4)`, aquí creamos un canal que puede guardar 4 elementos booleanos. Por lo tanto con este canal, somos capaces de enviar 4 elementos sin el bloqueo, pero la goroutine se bloqueará cuando intente enviar un quinto elemento y la goroutine no lo recibirá.
```
ch := make(chan type, n)
n == 0 ! non-bufferblock
n > 0 ! buffernon-block until n elements in the channel
```
Puedes probar con este código en tu computadora y cambiar algunos valores.
```
package main
import "fmt"
func main() {
c := make(chan int, 2) // si cambia 2 por 1 tendrá un error en tiempo de ejecución, pero 3 estará bien
c <- 1
c <- 2
fmt.Println(<-c)
fmt.Println(<-c)
}
```
## Range y Close
Podemos usar range para para hacer funcionar los 'buffer channels' como una lista y un map.
```
package main
import (
"fmt"
)
func fibonacci(n int, c chan int) {
x, y := 1, 1
for i := 0; i < n; i++ {
c <- x
x, y = y, x + y
}
close(c)
}
func main() {
c := make(chan int, 10)
go fibonacci(cap(c), c)
for i := range c {
fmt.Println(i)
}
}
```
`for i := range c` no parara de leer información de el canal hasta que el canal se alla cerrado. Vamos a usar la palabra reservada `close` para cerrar el canal en el ejemplo anterior. Es imposible enviar o recibir datos de un canal cerrado, puede usar `v, ok := <-ch` para verificar si el canal esta cerrado. Si `ok` devuelve false, esto significa que no hay datos en ese canal y este fue cerrado.
Recuerde cerrar siempre los canales productores, no los consumidores, o sera muy fácil obtener un estado de pánico o 'panic status'.
Otra cosa que deber tener que recordar es que los canales son diferentes a los archivos, y no debe cerrarlos con frecuencia, a menos que este seguro que es canal esta completamente sin uso, o desea salir del bloque donde usa 'range'.
## Select
En los ejemplos anteriores, nosotros usamos solo un canal, pero ¿cómo podemos lidiar con más de un canal? Go tiene la palabra reservada llamada `select` para escuchar muchos canales.
`select` de forma predeterminada es bloqueante, y este continua la ejecución solo cuando un canal tiene datos o recibió datos. Si varios canales están listos para usarse al mismo tiempo, select elegirá cual ejecutar al azar.
```
package main
import "fmt"
func fibonacci(c, quit chan int) {
x, y := 1, 1
for {
select {
case c <- x:
x, y = y, x + y
case <-quit:
fmt.Println("quit")
return
}
}
}
func main() {
c := make(chan int)
quit := make(chan int)
go func() {
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
quit <- 0
}()
fibonacci(c, quit)
}
```
`select` también tiene `default`, al igual que el `switch`. Cuando todos los canales no están listos para ser usados, ejecuta el default (no espera mas por el canal).
```
select {
case i := <-c:
// usa i
default:
// se ejecuta cuando c esta bloqueado
}
```
## Timeout
A veces la goroutine esta bloqueada, ¿pero como podemos evitar que esto, mientras tanto nos bloquee el programa? Podemos configurar para esto un timeout en el select.
```
func main() {
c := make(chan int)
o := make(chan bool)
go func() {
for {
select {
case v := <- c:
println(v)
case <- time.After(5 * time.Second):
println("timeout")
o <- true
break
}
}
}()
<- o
}
```
## Goroutine en tiempo de ejecución (o runtime)
El paquete `runtime` tiene algunas funciones para hacer frente a las goroutines.
- `runtime.Goexit()`
Sale de la actual goroutine, pero las funciones defer son ejecutadas como de costumbre.
- `runtime.Gosched()`
Permite que el manejador de tareas ejecute otras goroutines, y en algún momento vuelve allí.
- `runtime.NumCPU() int`
Devuelve el numero de núcleos del CPU
- `runtime.NumGoroutine() int`
Devuelve el numero de goroutines
- `runtime.GOMAXPROCS(n int) int`
Configura cuantos núcleos del CPU queremos usar
## Enlaces
- [Índice](preface.md)
- Sección anterior: [interfaces](02.6.md)
- Siguiente sección: [Resumen](02.8.md)

32
es/02.8.md Normal file
View File

@@ -0,0 +1,32 @@
# 2.8 Resumen
En este capitulo principalmente introducimos 25 palabras reservadas de Go. Vamos a repasar cuales son y que hacen.
break default func interface select
case defer go map struct
chan else goto package switch
const fallthrough if range type
continue for import return var
- `var` y `const` son usadas para definir variables y constantes.
- `package` e `import` son para el uso de paquetes.
- `func` es usada para definir funciones y métodos.
- `return` es usada para devolver valores en funciones o métodos.
- `defer` es usada para definir funciones defer.
- `go` es usada para comenzar a correr una nueva goroutine.
- `select` es usada para elegir entre múltiples canales de comunicación.
- `interface` es usada para definir una interfaz.
- `struct` es usada para definir tipos especiales personalizados.
- `break`, `case`, `continue`, `for`, `fallthrough`, `else`, `if`, `switch`, `goto`, `default` fueron introducidos en la sección 2.3.
- `chan` es usada para crear tipos de canales para comunicarse con las goroutines.
- `type` es usada para definir tipos personalizados.
- `map` es usada para definir un map que es como una tabla hash en otros lenguajes.
- `range` es usada para leer datos desde un `slice`, `map` o `channel`.
Si entendes como usar estas 25 palabras reservadas, usted ya a aprendido mucho sobre Go.
## Enlaces
- [Índice](preface.md)
- Sección anterior: [Concurrencia](02.7.md)
- Siguiente sección: [Conocimientos básicos sobre la Web](03.0.md)

9
es/03.0.md Normal file
View File

@@ -0,0 +1,9 @@
#3 Conocimientos básicos sobre la Web
La razón por la que estas leyendo este libro, es porque estas buscando aprender a desarrollar aplicaciones web con Go. Como dije antes, Go nos provee muchos paquetes con un gran potencial como es el `http`. Este nos ayudará mucho cuando queramos desarrollar aplicaciones web. En los siguientes capítulos te voy a enseñar todo lo que necesita saber, en este capítulo vamos a hablar sobre algunos conceptos sobre la web y como correr aplicaciones web en Go.
## Enlaces
- [Indice](preface.md)
- Capítulo anterior: [Capitulo 2 Resumen](02.8.md)
- Siguiente sección: [Principio para el trabajo en la Web](03.1.md)

155
es/03.1.md Normal file
View File

@@ -0,0 +1,155 @@
# 3.1. Principios para el trabajo en la Web

Cada vez que abres tu navegador, escribes alguna direccion URL y pulsas ENTER, verás hermosas páginas web que aparecen en pantalla. Pero ¿sabes lo que está sucediendo detrás de esta simple acción?
Normalmente, el navegador es un cliente, después de teclear la URL, envía peticiones a un servidor DNS, para obtener la dirección IP de la URL. Luego de encontrar el servidor en esta dirección IP, configura una conexión TCP. El navegador envía las peticiones HTTP a través de la conección. El servidor las maneja y responde con respuestas HTTP que conteienne el contenido que puedes ver en tu página web. Finalmente, el navegador renderiza el cuerpo de la página web y se desconecta del servidor.
![](images/3.1.web2.png?raw=true)
Figura 3.1 Procesos de usuarios que visitan una página web
Un servidor web también conocido como un servidor HTTP, utiliza el protocolo HTTP para comunicarse con los clientes. Todos los navegadores web pueden ser vistos como clientes.
Podemos dividir los principios de trabajo de Internet en los pasos siguientes:
- El cliente utiliza el protocolo TCP / IP para conectarse al servidor.
- El cliente envía paquetes de solicitud HTTP al servidor.
- El servidor devuelve los paquetes de respuesta HTTP para el cliente, si los recursos de petición incluyen scripts dinámicos , el servidor llama al motor de scripts primero.
- El cliente se desconecta del servidor, comienza renderizado HTML.
Este es un sencillo flujo de trabajo de asuntos HTTP, observe que el servidor despues de un tiempo cierra las conexiones de datos que se envían a los clientes, y espera a que la próxima petición.
## URL y la resolución de DNS
Siempre estamos utilizando URL para acceder a páginas web , pero ¿sabes cómo funciona el URL ?
El nombre completo de la dirección URL es Uniform Resource Locator (Localizador de Recursos Uniforme), esto es para la descripción de recursos en Internet. Su forma básica como sigue .
```
esquema://host[:port #]/ruta/.../[? cadena de consulta ][# ancla ]
esquema: asignación de protocolo subyacente (como HTTP , HTTPS, FTP )
host: IP o nombre de dominio del servidor HTTP
puerto#: puerto por defecto es 80 , y se puede omitir en este caso.
Si desea utilizar otros puertos , debe especificar qué puerto . Por ejemplo ,
http://www.cnblogs.com:8080/
ruta: ruta de los recursos
datos: datos de consulta se envían al servidor
ancla Ancla
```
DNS es la abreviatura de Domain Name Server (Servidor de Nombres de Dominio), que es el sistema de nombres de servicios informáticos en red, convierte nombres de dominio a direcciones IP reales, al igual que un traductor.
![](images/3.1.dns_hierachy.png?raw=true)
Figura 3.2 Principios de funcionamiento de DNS
Para entender más acerca de su principio de funcionamiento, veamos en detalle el proceso de resolución de DNS de la siguiente manera.
1. Después de escrito el nombre de dominio www.qq.com en el navegador , el sistema operativo comprueba si hay alguna relación de correspondencia en el archivo hosts para el nombre de dominio, si es así, termina la resolución de nombres de dominio.
2. Si no hay relación de proyección en el archivo hosts, el sistema operativo comprueba si hay alguna caché en el DNS, si es así, termina la resolución de nombres de dominio.
3. Si no hay relación entre el computador y el servirdor de caché DNS, el sistema operativo busca el primer servidor de resolución de DNS en la configuración de TCP / IP, que es el servidor DNS local en este momento. Cuando el servidor DNS local recibió consulta, si el nombre de dominio que desea consultar está contenida en la configuración local de los recursos regionales, a continuación, devuelve los resultados al cliente . Esta resolución DNS es autoritaria .
4. Si el servidor DNS local no contiene el nombre de dominio , y hay una relación de correspondencia en con el servidor caché DNS, el servidor DNS local devuelve este resultado a cliente. Esta resolución DNS no es autoritaria.
5. Si el servidor DNS local no puede resolver el nombre de dominio , ya sea por la configuración de los recursos regionales o caché, se procede al próximo paso, que depende de la configuración del servidor DNS local. Si el servidor local de DNS no permite seguir avanzando, el enruta la petición a la raiz del servidor DNS, entonces returna la dirección IP de un servidor DNS de alto nivel, que debería conocer el nombre de dominio, `.com` en este caso. Si el primer servidor de mas alto nivel no reconoce el nombre de dominio, de nuevo re enruta la petición al siguiente servidor DNS de mayor nivel, así sucesivamente hasta que encuentre un servidor DNS que pueda reconocer el nombre de dominio. Entonces, se le solicita al servidor DNS la IP correspondiente al dominio, en este caso `www.qq.com`
- Si el servidor DNS local habilita seguir adelante, envía la solicitud al servidor DNS de nivel superior , si el servidor DNS de nivel superior también no sabe el nombre de dominio , a continuación, seguira enviando solicitudes a nivel superior.
- Si el servidor DNS local permite a modo de avance , la dirección IP del servidor de nombre de dominio devuelve al servidor DNS local , y el servidor local envía a los clientes.
Sea que el cliente DNS local habilite o no el redireccionamiento, la dirección IP del dominio siempre se retornará al serviror local de DNS, y el servidor local DNS se lo enviará de vuelta al cliente
![](images/3.1.dns_inquery.png?raw=true)
Figura 3.3 DNS flujo de trabajo de resolución.
`Proceso de consulta recursiva` significa que las solicitudes de información están cambiando en el proceso, encambio las solicitudes de información no cambian en el proceso de consulta iterativa .
Ahora sabemos que los clientes obtendran direcciones IP , al final, por lo que los navegadores estan comunicandose con los servidores a través de las direcciones IP.
## Protocolo HTTP
Protocolo HTTP es la parte fundamental de los servicios web. Es importante saber lo que es el protocolo HTTP antes de entender cómo funciona la web.
HTTP es el protocolo que se utiliza para la comunicación entre navegadores y servidores web, se basa en el protocolo TCP, y por lo general utilizan el puerto 80 en el servidor web. Es un protocolo que utiliza el modelo de petición-respuesta, los clientes envían peticiones y el servidor responde. De acuerdo con el protocolo HTTP, los clientes siempre configuran una nueva conexión y envian una petición HTTP al servidor en cada asunto. Los servidor no son capaces de conectar con el cliente de forma proactiva, o establecer conexiones que inicien desde ellos. La conexión entre el cliente y el servidor puede ser cerrada por cualquier lado. Por ejemplo, puedes cancelar su tarea de descarga y conexión HTTP tu navegador se desconecta del servidor antes de que termine la descarga.
El protocolo HTTP no tiene estado, lo que significa que el servidor no tiene idea acerca de la relación entre dos conexiones, a pesar de que puedan ser ambas de un mismo cliente. Para solucionar este problema, las aplicaciones web usan Cookies para mantener el estado sostenible de conexiones.
Por esta causa, debido a que el protocolo HTTP se basa en el protocolo TCP , todos los ataques TCP afectarán a la comunicación HTTP en el servidor, como por ejemplo SYN Flood, DoS y DDoS .
### Paquete de solicitud HTTP (información del navegador).
Solicitar paquetes tienen tres partes: la línea de petición , solicitud de encabezado y cuerpo . Hay una línea en blanco entre la cabecera y el cuerpo.
```
GET/domains/ejemplo/ HTTP/1.1 // línea de solicitud : método de la petición , la dirección URL , el protocolo y su versión.
Host: www.iana.org / / nombre de dominio
User-Agent : Mozilla/5.0 (Windows NT 6.1 ) AppleWebKit/537.4 ( KHTML , like Gecko ) Chrome/22.0.1229.94 Safari/537.4 // información del navegador
Accept: text/html,application/xhtml+xml,application/xml;q=0,9,*/*;q=0.8 // MIME que los clientes pueden aceptar
Accept- Encoding : gzip, deflate, sdch // métodos de compresión
Accept- Charset : UTF- 8 , * ; q = 0.5 / / conjunto de caracteres en el lado del cliente
// Línea en blanco
// Cuerpo , los argumentos de recursos solicitud (por ejemplo , los argumentos de la POST )
```
Utilizamos fiddler que obtener la siguiente información de la solicitud.
![](images/3.1.http.png?raw=true)
Figura 3.4 Información de método GET capturado por fiddler
![](images/3.1.httpPOST.png?raw=true)
Figura 3.5 Información de método POST capturado por fiddler
**Podemos ver que el método GET no tiene cuerpo de la solicitud, el método POST si.**
Hay muchos métodos que puede utilizar para comunicarse con los servidores de HTTP ; GET, POST, PUT, DELETE son los 4 métodos básicos que utilizamos. Una URL representa un recurso en la red, por lo que estos 4 métodos significan consultar, cambiar, agregar y eliminar operaciones. GET y POST son los más utilizados en HTTP. GET puede añadir los parámetros de búsqueda usando el separador `?` para separa la URL de los parámetros y `&` entre parámetros como por ejemplo: `EditPosts.aspx?name=test1&id=123456`. POST puede colocar muchísima mas información en el cuerpo d ela petición, porque la URL solo puede tener un tamaño máximo, dependiendo del navegador. También, cuando pasamos nuestro nombre de usuario y contraseña , no queremos que este tipo de información aparesca en la URL, por lo que utilizamos la POST para mantenerlos invisibles.
### Paquete de respuesta HTTP (información del servidor)
Vamos a ver qué tipo de información se incluye en los paquetes de respuesta.
```
HTTP/1.1 200 OK // estado
Server : nginx/1.0.8 // web server y su versión en el equipo servidor
Date:Date: Tue, 30 Oct 2012 04:14:25 GMT // hora en que se respondió
Content-Type : text / html // tipo de datos que responde
Transfer-Encoding: chunked // significa que los datos se envían en fragmentos
Conexión : keep-alive // mantener la conexión
Content-Length: 90 // longitud del cuerpo
// Línea en blanco
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"... // cuerpo del mensaje
```
La primera línea se llama línea de estado , que tiene la versión de HTTP, el código de estado y el mensaje de estado.
El código de estado informa al cliente del estado de la respuesta del servidor HTTP, en HTTP/1.1 existen 5 tipos de códigos de estado
- 1xx Información
- 2xx Éxito
- 3xx Redirección
- 4xx Errores del CLiente
- 5xx Errores del Servidor
Vamos a ver más ejemplos sobre los paquetes de respuesta, 200 significa servidor respondió correctamente, 302 significa la redirección .
![](images/3.1.response.png?raw=true)
Figura 3.6 La información completa al visitar un sitio web
### HTTP no tiene estado y la conexion: keep-alive
Sin Estado no significa que el servidor no tiene la capacidad para mantener una conexión. Simplemente significa que el servidor no distingue la relación entre dos peticiones .
En HTTP/1.1 , Keep-alive se utiliza como valor predeterminado , si los clientes tienen más solicitudes, van a utilizar la misma conexión para muchas peticiones diferentes.
Observe que Keep Alive no puede mantener una conexión siempre, el software que se ejecuta en el servidor tiene cierto tiempo para mantener la conexión, y puedes cambiarlo.
## Solicitar instancia
![](images/3.1.web.png?raw=true)
Figura 3.7 Todos los paquetes para abrir una página Web
Podemos ver todo el proceso de comunicación entre el cliente y el servidor en la imagen superior. Puedes notar que hay muchos archivos de recursos en la lista, se les llama archivos estáticos, y Go ha especializado métodos de procesamiento de estos archivos.
Esta es la función más importante de los navegadores, solicitud de una URL y obtener datos de los servidores web, y luego renderizar HTML para tener una buena interfaz de usuario. Si encuentra algún archivo en el DOM, como archivos CSS o JS, los navegadores solicitarán estos recursos al servidor nuevamente, hasta que todos los recursos terminen de renderizarse en la pantalla.
Reducir las veces que se hacen peticiones HTTP es una manera de mejorar la velocidad de carga de las páginas. Al reducir el número de peticiones CSS y JS que tienen que ser cargadas, la latencia de petición y la presión en tu servidor web puede ser reducida al mismo tiempo
## Enlaces
- [Indice](preface.md)
- Sección anterior: [Conocimientos básicos sobre la Web](03.0.md)
- Siguiente sección: [Armando un servidor web sencillo](03.2.md)

65
es/03.2.md Normal file
View File

@@ -0,0 +1,65 @@
# 3.2 Armando un servidor web sencillo
Hablamos de que las aplicaciones web se basan en el protocolo HTTP, y Go permite la plena capacidad para HTTP en el paquete `net/http`, es muy fácil instalar un servidor web mediante el uso de este paquete .
## Uso del paquete http para configurar un servidor web
```
package main
import (
"fmt"
"net/http"
"strings"
"log"
)
func sayhelloName(w http.ResponseWriter, r *http.Request) {
r.ParseForm() //analizar argumentos , tiene que llamar a esto por su cuenta
fmt.Println(r.Form) //imprime información en el form en el lado del servidor
fmt.Println("path", r.URL.Path)
fmt.Println("scheme", r.URL.Scheme)
fmt.Println(r.Form["url_long"])
for k, v := range r.Form {
fmt.Println("key:", k)
fmt.Println("val:", strings.Join(v, ""))
}
fmt.Fprintf(w, "Hello astaxie!") // enviar datos al lado del cliente
}
func main() {
http.HandleFunc("/", sayhelloName) // define la ruta
err := http.ListenAndServe(":9090", nil) // establece el puerto de escucha
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
```
Después de ejecutar el código anterior, el host Local empieza a escuchar en el puerto 9090.
Abre tu navegador y visita `http://localhost:9090`, se puede ver que `Hello astaxie` está en su pantalla .
Vamos a intentar otra dirección con argumentos: `http://localhost:9090/?url_long=111&url_long=222`
Ahora veamos lo que sucedió en los dos lados de cliente y servidor.
Usted debe ver la siguiente información en su lado del servidor:
![](images/3.2.goweb.png?raw=true)
Figura 3.8 Información del servidor por pantalla
Como se puede ver, sólo tenemos que llamar a dos funciones para crear un servidor web simple.
Si has trabajado con PHP, es probable que te preguntes si necesitamos algo como Nginx o Apache, la respuesta es que no los necesitamos porque GO escucha el puerto TCP por sí mismo, y la función `sayhelloName` es la función lógica que funciona como controlador en PHP .
Si has trabajado con Python, debes estar familiarizado con tornado, y el ejemplo anterior es muy similar a tornado.
Si has trabajado con Ruby, usted puede notar que es como script/server en ROR.
Utilizamos dos funciones simples para configurar un servidor web simple en esta sección, y este servidor sencillo ya ha tenido capacidad para alta concurrencia. Vamos a hablar acerca de cómo usar esta característica en dos secciones siguientes .
## Enlaces
- [Indice](preface.md)
- Sección anterior: [Principios para el trabajo en la Web](03.1.md)
- Siguiente sección: [Como trabaja Go con la web](03.3.md)

85
es/03.3.md Normal file
View File

@@ -0,0 +1,85 @@
# 3.3 Como trabaja Go con la web
Hemos aprendido a utilizar el paquete `net/http` para construir un sencillo servidor web en la sección anterior, pero todos los principios de trabajo son las mismas que hemos hablado en la primera sección de este capítulo .
## Algunos conceptos de los principios de trabajo web
Request: solicitar datos de los usuarios, incluyendo la POST , GET, la Cookie y la URL.
Response: datos de respuesta desde el servidor a los clientes.
Conn: conexiones entre clientes y servidores.
Handler: lógica para la gestión de solicitudes y respuestas de productos.
## Mecanismo de funcionamiento del paquete http
La siguiente imagen muestra el flujo de trabajo del servidor web de Go.
![](images/3.3.http.png?raw=true)
Figura 3.9 flujo de trabajo http
1. Crear socket, escuchar en un puerto y esperar a los clientes .
2. Aceptar peticiones de los clientes .
3. Tramitar las solicitudes, leer el encabezado HTTP, si se utiliza el método POST, también es necesario para leer los datos en el cuerpo del mensaje y enviarlos a los controladores. Por último, devolver los datos de respuesta a los clientes.
Una vez que conocemos las respuestas a las preguntas siguientes , es fácil saber cómo funciona la web en Go.
- ¿Cómo escuchar un puerto?
- ¿Cómo aceptar peticiones de cliente ?
- ¿Cómo asignar controladores ?
En la sección anterior vimos que Go utiliza ListenAndServe para manejar estas etapas: inicializar un objeto de servidor , llamar a net.Listen( "tcp" , addr ) para configurar un puerto TCP de escucha y escuchar a la dirección y el puerto específico.
Vamos a echar un vistazo a el código fuente del paquete `http`.
```
//Build version go1.1.2.
func (srv *Server) Serve(l net.Listener) error {
defer l.Close()
var tempDelay time.Duration // how long to sleep on accept failure
for {
rw, e := l.Accept()
if e != nil {
if ne, ok := e.(net.Error); ok && ne.Temporary() {
if tempDelay == 0 {
tempDelay = 5 * time.Millisecond
} else {
tempDelay *= 2
}
if max := 1 * time.Second; tempDelay > max {
tempDelay = max
}
log.Printf("http: Accept error: %v; retrying in %v", e, tempDelay)
time.Sleep(tempDelay)
continue
}
return e
}
tempDelay = 0
c, err := srv.newConn(rw)
if err != nil {
continue
}
go c.serve()
}
}
```
¿Cómo aceptamos las peticiones de un cliente después que iniciamos a escuchar en un puerto? En el código fuente , podemos ver que se llama `srv.Serve(net.Listener)` para manejar peticiones de clientes. En el cuerpo de la función hay un `for{}`, se acepta la solicitud, se crea una nueva conexión y, a continuación, se inicia un nuevo goroutine , y pasa los datos de solicitud a esta goroutine: `go c.serve()`. Así es como Go es compatible con alta concurrencia, y cada goroutine es independiente.
¿Cómo usamos las funciones específicas para controlar las solicitudes? `conn` analiza la solicitud `c.ReadRequest()` al principio, y obtiene el controlador correspondiente `handler : = c.server.Handler` que es el segundo argumento que pasábamos cuando llamamos `ListenAndServe`. Como pasamos `nil`, Go usa su manejador por defecto `handler = DefaultServeMux`. Entonces, ¿qué está haciendo `DefaultServeMux` aquí? Bueno, esta es la variable de enrutador en este momento, se llama a las funciones de controlador de URL específicas . ¿Hemos asignado esto? Sí, lo hicimos. Recuerde que en la primera línea se utilizó `http.HandleFunc("/", sayhelloName)`. Es asi como se utiliza esta función para registrar el estado del router para la ruta "/". Cuando la dirección URL es `/`, el enrutador llama a la función `sayhelloName` . DefaultServeMux llama ServerHTTP para obtener la función de controlador de ruta diferente, y llama `sayhelloName` en este caso. Por último , el servidor escribe los datos y la respuesta a los clientes.
Flujo de trabajo detallado:
![](images/3.3.illustrator.png?raw=true)
Figura 3.10 Flujo de trabajo de manejar una petición HTTP
Creo que ahora debes saber cómo Go se ejecuta servidores web ahora.
## Enlaces
- [Indice](preface.md)
- Sección anterior: [Armando un servidor web sencillo](03.2.md)
- Siguiente sección: [Obteniendo el paquete http](03.4.md)

203
es/03.4.md Normal file
View File

@@ -0,0 +1,203 @@
# 3.4 Obteniendo el paquete http
En las secciones anteriores, hemos aprendido sobre el flujo de trabajo de la web, y hablamos un poco sobre el paquete `http`. En esta sección, vamos a aprender dos funciones básicas que estan en el paquete `http`: Conn, ServeMux.
## goroutine en Conn
A diferencia de los servidores HTTP normales, Go utiliza goroutine para toda trabajo inicializado por Conn con el fin de lograr una alta concurrencia y rendimiento, por lo que cada trabajo es independiente.
Go usa el siguiente código para esperar a nuevas conexiones de clientes .
```
c, err := srv.newConn(rw)
if err != nil {
continue
}
go c.serve()
```
Como puedes ver, se crea una goroutine para cada conexión , y se pasa el controlador que es capaz de leer los datos de solicitud a la goroutine.
## ServeMux personalizado
Utilizamos el enrutamiento por defecto en la sección anterior, cuando hablamos de conn.server, el router pasa los datos de solicitud como back-end al controlador.
El struct del router por defecto:
```
type ServeMux struct {
mu sync.RWMutex //debido a la concurrencia, tenemos que utilizar mutex aquí
m map[string]muxEntry //routers, cada asignación de cadena a un controlador
}
```
El struct de muxEntry:
```
type muxEntry struct {
explicit bool // exact match or not
h Handler
}
```
La interfaz de Handler:
```
type Handler interface {
ServeHTTP(ResponseWriter, *Request) // routing implementer
}
```
`Handler` es una interfaz, pero la función `sayhelloName` no implementa su interfaz, entonces ¿cómo podríamos agregarla como controlador? Debido a que hay otro tipo `HandlerFunc` en el paquete `http`. Nosotros llamamos `HandlerFunc` para definir nuestro método `sayhelloName` , así `sayhelloName` implementa el `Handler` al mismo tiempo. Es como si llamaramos `HandlerFunc(f)`, y la función `f` es forzado a convertirce al tipo `HandlerFunc`.
```
type HandlerFunc func(ResponseWriter, *Request)
// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
```
¿Cómo enrutador llama los controladores después de establecer reglas del router?
El enrutador llama `mux.handler.ServeHTTP(w , r)` cuando recibe solicitudes. En otras palabras, se llama la interfaz `ServeHTTP` de los controladores.
Ahora, vamos a ver cómo funciona `mux.handler`.
```
func (mux *ServeMux) handler(r *Request) Handler {
mux.mu.RLock()
defer mux.mu.RUnlock()
// Host-specific pattern takes precedence over generic ones
h := mux.match(r.Host + r.URL.Path)
if h == nil {
h = mux.match(r.URL.Path)
}
if h == nil {
h = NotFoundHandler()
}
return h
}
```
El enrutador utiliza la URL como llave para encontrar el controlador correspondiente que guarda en un mapa, luego llama handler.ServeHTTP para ejecutar funciones y manejar los datos.
En este punto, debes entender el flujo de trabajo del enrutador, y Go realmente apoya routers personalizados. El segundo argumento de ListenAndServe es para la configuración del enrutadores a la medida, entonces cualquier enrutador que implemente la interfaz de `Handler` puede ser utilizado.
El siguiente ejemplo muestra cómo implementar un enrutador sencillo.
```
package main
import (
"fmt"
"net/http"
)
type MyMux struct {
}
func (p *MyMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/" {
sayhelloName(w, r)
return
}
http.NotFound(w, r)
return
}
func sayhelloName(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello myroute!")
}
func main() {
mux := &MyMux{}
http.ListenAndServe(":9090", mux)
}
```
# Enrutamiento
Si no quieres usar un enrutador, todavía puedes lograr lo que escribimos en la sección de arriba reemplazando el segundo argumento de `ListenAndServe` a nil, y registrando las URLS usando un `HandleFunc` función que recorre todas las URLs registradas para encontrar la mejor coincidencia, entonces debemos preocuparnos por el orden de registro.
código de ejemplo:
```
http.HandleFunc("/", views.ShowAllTasksFunc)
http.HandleFunc("/complete/", views.CompleteTaskFunc)
http.HandleFunc("/delete/", views.DeleteTaskFunc)
//ShowAllTasksFunc es usado para manejar la URL "/" que tiene por defecto todo
//TODO agregar manejador para 404
func ShowAllTasksFunc(w http.ResponseWriter, r *http.Request) {
if r.Method == "GET" {
context := db.GetTasks("pending") //true cuando tu no quieres eliminar tareas
//db es un paquete que interactua con la base de datos
if message != "" {
context.Message = message
}
homeTemplate.Execute(w, context)
message = ""
} else {
message = "Method not allowed"
http.Redirect(w, r, "/", http.StatusFound)
}
}
```
Esto está bien para aplicaciones simples las cuales no necesitan ruteos parametrizados, pero ¿cuándo necesitas eso? Puedes usar las herramientas o frameworks, pero como este libro está enfocado en crear aplicaciones web en Go, vamos a enseñarte como manejar ese escenario también.
Cuando la concidencia es hecha, se llama a la función definida en `HandleFunc`, así que supongamos que estamos escribiendo un manejador para una lista y queremos eliminar una tarea, así que la aplicación decide que función se va a llamar cuando llegue la petición `/delete/1`, entonces registramos la URL de la siguiente manera:
`http.HandleFunc("/delete/", views.DeleteTaskFunc)`
`/delete/1` this URL matches closest with the "/delete/" URL than any other URL so in the `r.URL.path` we get the entire URL of the request.
```
http.HandleFunc("/delete/", views.DeleteTaskFunc)
//DeleteTaskFunc is used to delete a task, trash = move to recycle bin, delete = permanent delete
func DeleteTaskFunc(w http.ResponseWriter, r *http.Request) {
if r.Method == "GET" {
id := r.URL.Path[len("/delete/"):]
if id == "all" {
db.DeleteAll()
http.Redirect(w, r, "/", http.StatusFound)
} else {
id, err := strconv.Atoi(id)
if err != nil {
fmt.Println(err)
} else {
err = db.DeleteTask(id)
if err != nil {
message = "Error deleting task"
} else {
message = "Task deleted"
}
http.Redirect(w, r, "/", http.StatusFound)
}
}
} else {
message = "Method not allowed"
http.Redirect(w, r, "/", http.StatusFound)
}
}
```
Enlace: https://github.com/thewhitetulip/Tasks/blob/master/views/views.go#L170-#L195
Este método lo que hace básicamente es que la función que maneja la URL `/delete/`, se toma la URL completa, que es `/delete/1`, se toman segmentos de la cadena y se extraen todo lo que después de la cadena de coincidencia, en este caso es `1`. Entonces usamos el paquete `strconv` para convertir la cadena en entero y eliminar la tarea con ese identificador.
En escenarios mas complejos también podemos utilizar este método, la ventaja es que no vamos a tener que usar herramientas de terceros, pero las herramientas de terceros tienden a ser útiles en su sentido propio. Tienes que tomar la decisión de cuál método prefieres. Ninguna respuesta es la respuesta correcta.
## Flujo de ejecución del código en GO
Vamos a echar un vistazo a la lista de flujo de ejecución en conjunto.
- Se llama http.HandleFunc
1. Se Llama HandleFunc de DefaultServeMux
2. Se Llama Handle de DefaultServeMux
3. Se agregan las reglas del enrutamiento a map[string]muxEntry de DefaultServeMux
- Se llama http.ListenAndServe (":9090" , nil )
1. Se instancia el servidor
2. Llama ListenAndServe del Servidor
3. Llama net.Listen ( " tcp" , addr ) para escuchar en el puerto .
4. Iniciar un bucle, y aceptar las solicitudes en el cuerpo del bucle.
5. Instanciada una Conn se empieza una goroutine para cada solicitud : ir c.serve ().
6. Lee petición de datos : w , err : = c.readRequest ().
7. Comprueba si el controlador está vacío, si está vacíoutiliza DefaultServeMux .
8. Llama al controlador de ServeHTTP
9. Ejecutar código en DefaultServeMux en este caso.
10. Elije el controlador URL y ejecutar código del controlador en esta seccion: mux.handler.ServeHTTP ( w , r)
11. Cómo elegir handler:
A. Normas de router de verificación para esta URL.
B. Llamar ServeHTTP en ese controlador, si es que existe.
C. Llamar ServeHTTP de NotFoundHandler lo contrario.
## Enlaces
- [Indice](preface.md)
- Sección anterior: [Como trabaja Go con la web](03.3.md)
- Siguiente sección: [Resumen](03.5.md)

11
es/03.5.md Normal file
View File

@@ -0,0 +1,11 @@
# 3.5 Resumen
En este capítulo, presentamos HTTP, el flujo de la resolución DNS y cómo construir un servidor Web simple. Entonces hablamos de cómo Go implementa servidor web para nosotros al ver el código fuente del paquete net/http.
Espero que ahora conozcas mucho más sobre el desarrollo web, y deberías ver que es muy fácil y flexible crear una aplicación web en Go.
## Enlaces
- [Indice](preface.md)
- Sección anterior: [Obteniendo el paquete http](03.4.md)
- Siguiente sección: [Formulario de entrada de los usuario](04.0.md)

23
es/04.0.md Normal file
View File

@@ -0,0 +1,23 @@
# 4 Formularios de Usuarios
Un formulario de usuario es algo que es usado comunmente cuando desarrollamos aplicaciones web. El provee la habilidad de comunicarse entre clientes y servidores. Debes estar familiarizado con los formularios si eres un desarrollador web; si eres un programador C/C++ te puedes estar preguntando, ¿qué es un formulario de usuario?
Un formulario es un área que contiene elementos de de formulario. Los usuarios pueden ingresar información en los elementos como en campos de texto, listas desplegables, botones circulares, cajas de chequeo, etc. nOsotros utilizamos la etiqueta `<form>` para definir formularios.
```
<form>
...
elementos de entrada
...
</form>
```
Go tiene muchas funciones muy convenientes para lidiar con formularios de usuario. Facilmente puedes obtener la información de un formulario en una petición HTTP, y se pueden integrar fácilmente en tus aplicaciones propias. En la sección 4.1, vamos a hablar de como manejar la información de los formularios en Go. También que no puedes confiar en cualquier datao que viene del lado del cliente, primero debes validar los datos antes de usarlos. Vamos a ir a través de algunos ejemplos de como podemos validar la información de los formularios en la sección 4.2.
Decimos que HTTP es un procolo sin estado. ¿Cómo podemos identificar que varios formularios vienen del mismo usuario? y ¿cómo nos damos cuenta que un formulario solo puede ser enviado una vez? Miraremos algunos detalles concernientes a las cookies (una cookie es un segmento de información que puede ser guardado en el lado del cluente y agregado al encabezado de la petición cuando la petición es enviada al servidor) en los capítulos 4.3 y 4.4.
Otro caso de uso común para los formularios es subir archivos. En la sección 4.5, aprenderás como hacer esto y también como controlar el tamaño del archivo que vas a subir antes de subirlo, en Go.
## Links
- [Índice](preface.md)
- Sección anterior: [Resumen](03.5.md)
- Siguiente sección: [Procesando la entrada de los formularios](04.1.md)

107
es/04.1.md Normal file
View File

@@ -0,0 +1,107 @@
# 4.1 Procesando las entradas de los formularios
Antes de comenzar, vamos a echarle un vistazo a un formulario típico de usuario, guardado como `login.gtpl` en tu carpeta de proyecto.
```
<html>
<head>
<title></title>
</head>
<body>
<form action="/login" method="post">
Nombre de Usuario:<input type="text" name="username">
Contraseña:<input type="password" name="password">
<input type="submit" value="Ingresar">
</form>
</body>
</html>
```
Este formulario va a ser enviado a `/login` en el servidor. Después que el usuario de click en el botón Ingresar, la información va a ser enviada al manejador `login` registrado en nuestro enrutador. Entonces necesitamos saber qué método se usa, el POST o el GET.
Es fácil darnos cuenta usando el paquete `http`. Veamos como manejar los formularios en la página de Ingreso.
```
package main
import (
"fmt"
"html/template"
"log"
"net/http"
"strings"
)
func sayhelloName(w http.ResponseWriter, r *http.Request) {
r.ParseForm() // Análizar los parámetros URL enviados, entonces analizar el paquete para analizar el cuerpo del paquete, para peticiones POST.
// precaución: Si no se llama al método ParseForm, la siguiente unformación no podra ser obtenida del Formulario
fmt.Println(r.Form) // Imprime la información del lado del servidor
fmt.Println("path", r.URL.Path)
fmt.Println("scheme", r.URL.Scheme)
fmt.Println(r.Form["url_long"])
for k, v := range r.Form {
fmt.Println("key:", k)
fmt.Println("val:", strings.Join(v, ""))
}
fmt.Fprintf(w, "Hello astaxie!") // Escrite la respuesta
}
func login(w http.ResponseWriter, r *http.Request) {
fmt.Println("method:", r.Method) //get request method
if r.Method == "GET" {
t, _ := template.ParseFiles("login.gtpl")
t.Execute(w, nil)
} else {
r.ParseForm()
// Parte lógica de la función login
fmt.Println("username:", r.Form["username"])
fmt.Println("password:", r.Form["password"])
}
}
func main() {
http.HandleFunc("/", sayhelloName) // Definimos la regla del enrutador
http.HandleFunc("/login", login)
err := http.ListenAndServe(":9090", nil) // Definimos el puerto de escucha
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
```
Aquí usamos el método `r.Methoc` para obtener el tipo de petición, el cual retornará uno de los siguientes verbos: "GET", "POST", "PUT", etc.
En la función `login`, usamos `r.Method` para verificar si es una petición a la página o una petición para procesar los datos. En otras palabras, verificamos si el usuario solo abró la página o está intentando ingresar. Únicamente mostramos la página cuando viene desde un método GET, y ejecuta la lógica cuando viene un método POST.
Podrás ver la interfaz cuando visitas `http://127.0.0.1:9090/login` en tu navegador.
![](images/4.1.login.png?raw=true)
Figure 4.1 Interfaz de ingreso de usuario
El servidor no va a imprimir nada hasta que nosotros escribamos un usuario y contraseña, porque el manejador no analizará los datos hasta que llamemos `r.ParseForm()`. Agreguemos `r.ParseForm()` antes de `fmt.Println("username:", r.Form["username"])`, compilemos el programa e intentémolo de nuevo. Ahora puedes ve la información en la consola del servidor.
`r.Form` contiene todos los argumentos de la petición, por ejemplo la cadena de consulta en la URL y la información en el POST y PUT. Si la información tiene conflictos, como parámetros que tienen el mismo nombre, el servidor almacenará la información en un segmento con múltiples valores. La documentación de Go dice que Go guardará la información de las peticiones GET y el POST en diferentes lugares.
Trata de cambiar la URL de ingreso de `http://127.0.0.1:9090/login` a `http://127.0.0.1:9090/login?username=astaxie` en el archivo `login.gtpl`, prueba de nuevo, Y podrás ver el segmento en el lado del servidor.
![](images/4.1.slice.png?raw=true)
Figure 4.2 Server prints request data
El tipo de `request.Form` es `url.Value`. Y lo guarda en el formato `llave=valor`.
```
v := url.Values{}
v.Set("name", "Ava")
v.Add("friend", "Jess")
v.Add("friend", "Sarah")
v.Add("friend", "Zoe")
// v.Encode() == "name=Ava&friend=Jess&friend=Sarah&friend=Zoe"
fmt.Println(v.Get("name"))
fmt.Println(v.Get("friend"))
fmt.Println(v["friend"])
```
**Recomendaciones** Las peticiones tienen la habilidad de acceder información por medio del método `FormValue()`. Por ejemplo, puedes cambiar de `r.Form["username"]` a `r.FormValue("username")`, y Go llamará `r.ParseForm` automáticamente. Nota que se retornará el primer valor, si existen varias llaves con el mismo nombre y retornará una cadena vacía si no existe el argumento.
## Links
- [Indice](preface.md)
- Sección anterior: [Formulario de entrada de los usuario](04.0.md)
- Siguiente sección: [Verificando las entradas](04.2.md)

96
es/preface.md Normal file
View File

@@ -0,0 +1,96 @@
- 1.[Go environment configuration](01.0.md)
- 1.1. [Installation](01.1.md)
- 1.2. [$GOPATH and workspace](01.2.md)
- 1.3. [Go commands](01.3.md)
- 1.4. [Go development tools](01.4.md)
- 1.5. [Summary](01.5.md)
- 2.[Go basic knowledge](02.0.md)
- 2.1. ["Hello, Go"](02.1.md)
- 2.2. [Go foundation](02.2.md)
- 2.3. [Control statements and functions](02.3.md)
- 2.4. [struct](02.4.md)
- 2.5. [Object-oriented](02.5.md)
- 2.6. [interface](02.6.md)
- 2.7. [Concurrency](02.7.md)
- 2.8. [Summary](02.8.md)
- 3.[Web foundation](03.0.md)
- 3.1. [Web working principles](03.1.md)
- 3.2. [Build a simple web server](03.2.md)
- 3.3. [How Go works with web](03.3.md)
- 3.4. [Get into http package](03.4.md)
- 3.5. [Summary](03.5.md)
- 4.[User form](04.0.md)
- 4.1. [Process form inputs](04.1.md)
- 4.2. [Verification of inputs](04.2.md)
- 4.3. [Cross site scripting](04.3.md)
- 4.4. [Duplicate submissions](04.4.md)
- 4.5. [File upload](04.5.md)
- 4.6. [Summary](04.6.md)
- 5.[Database](05.0.md)
- 5.1. [database/sql interface](05.1.md)
- 5.2. [MySQL](05.2.md)
- 5.3. [SQLite](05.3.md)
- 5.4. [PostgreSQL](05.4.md)
- 5.5. [Develop ORM based on beedb](05.5.md)
- 5.6. [NoSQL database](05.6.md)
- 5.7. [Summary](05.7.md)
- 6.[Data storage and session](06.0.md)
- 6.1. [Session and cookies](06.1.md)
- 6.2. [How to use session in Go](06.2.md)
- 6.3. [Session storage](06.3.md)
- 6.4. [Prevent hijack of session](06.4.md)
- 6.5. [Summary](06.5.md)
- 7.[Text files](07.0.md)
- 7.1. [XML](07.1.md)
- 7.2. [JSON](07.2.md)
- 7.3. [Regexp](07.3.md)
- 7.4. [Templates](07.4.md)
- 7.5. [Files](07.5.md)
- 7.6. [Strings](07.6.md)
- 7.7. [Summary](07.7.md)
- 8.[Web services](08.0.md)
- 8.1. [Sockets](08.1.md)
- 8.2. [WebSocket](08.2.md)
- 8.3. [REST](08.3.md)
- 8.4. [RPC](08.4.md)
- 8.5. [Summary](08.5.md)
- 9.[Security and encryption](09.0.md)
- 9.1. [CSRF attacks](09.1.md)
- 9.2. [Filter inputs](09.2.md)
- 9.3. [XSS attacks](09.3.md)
- 9.4. [SQL injection](09.4.md)
- 9.5. [Password storage](09.5.md)
- 9.6. [Encrypt and decrypt data](09.6.md)
- 9.7. [Summary](09.7.md)
- 10.[Internationalization and localization](10.0.md)
- 10.1 [Time zone](10.1.md)
- 10.2 [Localized resources](10.2.md)
- 10.3 [International sites](10.3.md)
- 10.4 [Summary](10.4.md)
- 11.[Error handling, debugging and testing](11.0.md)
- 11.1. [Error handling](11.1.md)
- 11.2. [Debugging by using GDB](11.2.md)
- 11.3. [Write test cases](11.3.md)
- 11.4. [Summary](11.4.md)
- 12.[Deployment and maintenance](12.0.md)
- 12.1. [Logs](12.1.md)
- 12.2. [Errors and crashes](12.2.md)
- 12.3. [Deployment](12.3.md)
- 12.4. [Backup and recovery](12.4.md)
- 12.5. [Summary](12.5.md)
- 13.[Build a web framework](13.0.md)
- 13.1. [Project program](13.1.md)
- 13.2. [Customized routers](13.2.md)
- 13.3. [Design controllers](13.3.md)
- 13.4. [Logs and configurations](13.4.md)
- 13.5. [Add, delete and update blogs](13.5.md)
- 13.6. [Summary](13.6.md)
- 14.[Develop web framework](14.0.md)
- 14.1. [Static files](14.1.md)
- 14.2. [Session](14.2.md)
- 14.3. [Form](14.3.md)
- 14.4. [User validation](14.4.md)
- 14.5. [Multi-language support](14.5.md)
- 14.6. [pprof](14.6.md)
- 14.7. [Summary](14.7.md)
- Appendix A [References](ref.md)

View File

@@ -7,6 +7,7 @@
这就像一个传统,在学习大部分语言之前,你先学会如何编写一个可以输出`hello world`的程序。
准备好了吗Let's Go!
```Go
package main
@@ -15,7 +16,7 @@
func main() {
fmt.Printf("Hello, world or 你好,世界 or καλημ ́ρα κóσμ or こんにちはせかい\n")
}
```
输出如下:
Hello, world or 你好,世界 or καλημ ́ρα κóσμ or こんにちはせかい

View File

@@ -7,29 +7,34 @@
Go语言里面定义变量有多种方式。
使用`var`关键字是Go最基本的定义变量方式与C语言不同的是Go把变量类型放在变量名后面
```Go
//定义一个名称为“variableName”类型为"type"的变量
var variableName type
```
定义多个变量
```Go
//定义三个类型都是“type”的变量
var vname1, vname2, vname3 type
```
定义变量并初始化值
```Go
//初始化“variableName”的变量为“value”值类型是“type”
var variableName type = value
```
同时初始化多个变量
```Go
/*
定义三个类型都是"type"的变量,并且分别初始化为相应的值
vname1为v1vname2为v2vname3为v3
*/
var vname1, vname2, vname3 type= v1, v2, v3
```
你是不是觉得上面这样的定义有点繁琐没关系因为Go语言的设计者也发现了有一种写法可以让它变得简单一点。我们可以直接忽略类型声明那么上面的代码变成这样了
```Go
/*
定义三个变量,它们分别初始化为相应的值
@@ -37,8 +42,9 @@ Go语言里面定义变量有多种方式。
然后Go会根据其相应值的类型来帮你初始化它们
*/
var vname1, vname2, vname3 = v1, v2, v3
```
你觉得上面的还是有些繁琐?好吧,我也觉得。让我们继续简化:
```Go
/*
定义三个变量,它们分别初始化为相应的值
@@ -46,7 +52,7 @@ Go语言里面定义变量有多种方式。
编译器会根据初始化的值自动推导出相应的类型
*/
vname1, vname2, vname3 := v1, v2, v3
```
现在是不是看上去非常简洁了?`:=`这个符号直接取代了`var``type`,这种形式叫做简短声明。不过它有一个限制,那就是它只能用在函数内部;在函数外部使用则会无法编译通过,所以一般用`var`方式来定义全局变量。
`_`(下划线)是个特殊的变量名,任何赋予它的值都会被丢弃。在这个例子中,我们将值`35`赋予`b`,并同时丢弃`34`
@@ -54,30 +60,33 @@ Go语言里面定义变量有多种方式。
_, b := 34, 35
Go对于已声明但未使用的变量会在编译阶段报错比如下面的代码就会产生一个错误声明了`i`但未使用。
```Go
package main
func main() {
var i int
}
```
## 常量
所谓常量也就是在程序编译阶段就确定下来的值而程序在运行时无法改变该值。在Go程序中常量可定义为数值、布尔值或字符串等类型。
它的语法如下:
```Go
const constantName = value
//如果需要,也可以明确指定常量的类型:
const Pi float32 = 3.1415926
```
下面是一些常量声明的例子:
```Go
const Pi = 3.1415926
const i = 10000
const MaxThread = 10
const prefix = "astaxie_"
```
Go 常量和一般程序语言不同的是,可以指定相当多的小数位数(例如200位)
若指定給float32自动缩短为32bit指定给float64自动缩短为64bit详情参考[链接](http://golang.org/ref/spec#Constants)
@@ -86,6 +95,7 @@ Go 常量和一般程序语言不同的是,可以指定相当多的小数位
### Boolean
在Go中布尔值的类型为`bool`,值是`true``false`,默认为`false`
```Go
//示例代码
var isActive bool // 全局变量声明
@@ -95,7 +105,7 @@ Go 常量和一般程序语言不同的是,可以指定相当多的小数位
valid := false // 简短声明
available = true // 赋值操作
}
```
### 数值类型
@@ -116,15 +126,17 @@ Go 常量和一般程序语言不同的是,可以指定相当多的小数位
浮点数的类型有`float32``float64`两种(没有`float`类型),默认是`float64`
这就是全部吗NoGo还支持复数。它的默认类型是`complex128`64位实数+64位虚数。如果需要小一些的也有`complex64`(32位实数+32位虚数)。复数的形式为`RE + IMi`,其中`RE`是实数部分,`IM`是虚数部分,而最后的`i`是虚数单位。下面是一个使用复数的例子:
```Go
var c complex64 = 5+5i
//output: (5+5i)
fmt.Printf("Value is: %v", c)
```
### 字符串
我们在上一节中讲过Go中的字符串都是采用`UTF-8`字符集编码。字符串是用一对双引号(`""`)或反引号(`` ` `` `` ` ``)括起来定义,它的类型是`string`。
```Go
//示例代码
var frenchHello string // 声明变量为字符串的一般方法
@@ -134,35 +146,39 @@ Go 常量和一般程序语言不同的是,可以指定相当多的小数位
japaneseHello := "Konichiwa" // 同上
frenchHello = "Bonjour" // 常规赋值
}
```
在Go中字符串是不可变的例如下面的代码编译时会报错cannot assign to s[0]
```Go
var s string = "hello"
s[0] = 'c'
```
但如果真的想要修改怎么办呢?下面的代码可以实现:
```Go
s := "hello"
c := []byte(s) // 将字符串 s 转换为 []byte 类型
c[0] = 'c'
s2 := string(c) // 再转换回 string 类型
fmt.Printf("%s\n", s2)
```
Go中可以使用`+`操作符来连接两个字符串:
```Go
s := "hello,"
m := " world"
a := s + m
fmt.Printf("%s\n", a)
```
修改字符串也可写为:
```Go
s := "hello"
s = "c" + s[1:] // 字符串虽不能更改,但可进行切片操作
fmt.Printf("%s\n", s)
```
如果要声明一个多行的字符串怎么办?可以通过`` ` ``来声明:
m := `hello
@@ -175,12 +191,13 @@ Go中可以使用`+`操作符来连接两个字符串:
### 错误类型
Go内置有一个`error`类型专门用来处理错误信息Go的`package`里面还专门有一个包`errors`来处理错误:
```Go
err := errors.New("emit macho dwarf: elf header corrupted")
if err != nil {
fmt.Print(err)
}
```
### Go数据底层的存储
下面这张图来源于[Russ Cox Blog](http://research.swtch.com/)中一篇介绍[Go数据结构](http://research.swtch.com/godata)的文章,大家可以看到这些基础类型底层都是分配了一块内存,然后存储了相应的值。
@@ -196,6 +213,7 @@ Go内置有一个`error`类型专门用来处理错误信息Go的`package`
在Go语言中同时声明多个常量、变量或者导入多个包时可采用分组的方式进行声明。
例如下面的代码:
```Go
import "fmt"
import "os"
@@ -207,8 +225,9 @@ Go内置有一个`error`类型专门用来处理错误信息Go的`package`
var i int
var pi float32
var prefix string
```
可以分组写成如下形式:
```Go
import(
"fmt"
@@ -226,10 +245,11 @@ Go内置有一个`error`类型专门用来处理错误信息Go的`package`
pi float32
prefix string
)
```
### iota枚举
Go里面有一个关键字`iota`,这个关键字用来声明`enum`的时候采用它默认开始值是0const中每增加一行加1
```Go
const(
x = iota // x == 0
@@ -251,7 +271,7 @@ Go里面有一个关键字`iota`,这个关键字用来声明`enum`的时候采
d,e,f = iota,iota,iota //d=3,e=3,f=3
g //g = 4
```
>除非被显式设置为其它值或`iota`,每个`const`分组的第一个常量被默认设置为它的0值第二及后续的常量被默认设置为它前面那个常量的值如果前面那个常量的值是`iota`,则它也被设置为`iota`。
### Go程序设计的一些规则
@@ -263,35 +283,39 @@ Go之所以会那么简洁是因为它有一些默认的行为
### array
`array`就是数组,它的定义方式如下:
```Go
var arr [n]type
```
在`[n]type`中,`n`表示数组的长度,`type`表示存储元素的类型。对数组的操作和其它语言类似,都是通过`[]`来进行读取或赋值:
```Go
var arr [10]int // 声明了一个int类型的数组
arr[0] = 42 // 数组下标是从0开始的
arr[1] = 13 // 赋值操作
fmt.Printf("The first element is %d\n", arr[0]) // 获取数据返回42
fmt.Printf("The last element is %d\n", arr[9]) //返回未赋值的最后一个元素默认返回0
```
由于长度也是数组类型的一部分,因此`[3]int`与`[4]int`是不同的类型,数组也就不能改变长度。数组之间的赋值是值的赋值,即当把一个数组作为参数传入函数的时候,传入的其实是该数组的副本,而不是它的指针。如果要使用指针,那么就需要用到后面介绍的`slice`类型了。
数组可以使用另一种`:=`来声明
```Go
a := [3]int{1, 2, 3} // 声明了一个长度为3的int数组
b := [10]int{1, 2, 3} // 声明了一个长度为10的int数组其中前三个元素初始化为1、2、3其它默认为0
c := [...]int{4, 5, 6} // 可以省略长度而采用`...`的方式Go会自动根据元素个数来计算长度
```
也许你会说我想数组里面的值还是数组能实现吗当然咯Go支持嵌套数组即多维数组。比如下面的代码就声明了一个二维数组
```Go
// 声明了一个二维数组该数组以两个数组作为元素其中每个数组中又有4个int类型的元素
doubleArray := [2][4]int{[4]int{1, 2, 3, 4}, [4]int{5, 6, 7, 8}}
// 上面的声明可以简化,直接忽略内部的类型
easyArray := [2][4]int{{1, 2, 3, 4}, {5, 6, 7, 8}}
```
数组的分配如下所示:
![](images/2.2.array.png?raw=true)
@@ -304,15 +328,18 @@ Go之所以会那么简洁是因为它有一些默认的行为
在很多应用场景中数组并不能满足我们的需求。在初始定义数组时我们并不知道需要多大的数组因此我们就需要“动态数组”。在Go里面这种数据结构叫`slice`
`slice`并不是真正意义上的动态数组,而是一个引用类型。`slice`总是指向一个底层`array``slice`的声明也可以像`array`一样,只是不需要长度。
```Go
// 和声明array一样只是少了长度
var fslice []int
```
接下来我们可以声明一个`slice`,并初始化数据,如下所示:
```Go
slice := []byte {'a', 'b', 'c', 'd'}
```
`slice`可以从一个数组或一个已经存在的`slice`中再次声明。`slice`通过`array[i:j]`来获取,其中`i`是数组的开始位置,`j`是结束位置,但不包含`array[j]`,它的长度是`j-i`。
```Go
// 声明一个含有10个元素元素类型为byte的数组
var ar = [10]byte {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'}
@@ -327,7 +354,7 @@ Go之所以会那么简洁是因为它有一些默认的行为
// b是数组ar的另一个slice
b = ar[3:5]
// b的元素是ar[3]和ar[4]
```
>注意`slice`和数组在声明时的区别:声明数组时,方括号内写明了数组的长度或使用`...`自动计算长度,而声明`slice`时,方括号内没有任何字符。
它们的数据结构如下所示
@@ -343,6 +370,7 @@ slice有一些简便的操作
- 如果从一个数组里面直接获取`slice`,可以这样`ar[:]`因为默认第一个序列是0第二个是数组的长度即等价于`ar[0:len(ar)]`
下面这个例子展示了更多关于`slice`的操作:
```Go
// 声明一个数组
var array = [10]byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'}
@@ -360,17 +388,18 @@ slice有一些简便的操作
bSlice = aSlice[:3] // bSlice 包含 aSlice[0], aSlice[1], aSlice[2] 也就是含有: d,e,f
bSlice = aSlice[0:5] // 对slice的slice可以在cap范围内扩展此时bSlice包含d,e,f,g,h
bSlice = aSlice[:] // bSlice包含所有aSlice的元素: d,e,f,g
```
`slice`是引用类型,所以当引用改变其中元素的值时,其它的所有引用都会改变该值,例如上面的`aSlice`和`bSlice`,如果修改了`aSlice`中元素的值,那么`bSlice`相对应的值也会改变。
从概念上面来说`slice`像一个结构体,这个结构体包含了三个元素:
- 一个指针,指向数组中`slice`指定的开始位置
- 长度,即`slice`的长度
- 最大长度,也就是`slice`开始位置到数组的最后位置的长度
```Go
Array_a := [10]byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'}
Slice_a := Array_a[2:5]
```
上面代码的真正存储结构如下图所示
![](images/2.2.slice2.png?raw=true)
@@ -388,10 +417,11 @@ slice有一些简便的操作
但当`slice`中没有剩余空间(即`(cap-len) == 0`)时,此时将动态分配新的数组空间。返回的`slice`数组指针将指向这个空间,而原数组的内容将保持不变;其它引用此数组的`slice`则不受影响。
从Go1.2开始slice支持了三个参数的slice之前我们一直采用这种方式在slice或者array基础上来获取一个slice
```Go
var array [10]int
slice := array[2:4]
```
这个例子里面slice的容量是8新版本里面可以指定这个容量
slice = array[2:4:7]
@@ -405,6 +435,7 @@ slice有一些简便的操作
`map`也就是Python中字典的概念它的格式为`map[keyType]valueType`
我们看下面的代码,`map`的读取和设置也类似`slice`一样,通过`key`来操作,只是`slice`的`index`只能是int类型而`map`多了很多类型,可以是`int`,可以是`string`及所有完全定义了`==`与`!=`操作的类型。
```Go
// 声明一个key是字符串值为int的字典,这种方式的声明需要在使用之前使用make初始化
var numbers map[string]int
@@ -416,6 +447,7 @@ slice有一些简便的操作
fmt.Println("第三个数字是: ", numbers["three"]) // 读取数据
// 打印出来如:第三个数字是: 3
```
这个`map`就像我们平常看到的表格一样,左边列是`key`,右边列是值
@@ -430,6 +462,8 @@ slice有一些简便的操作
通过`delete`删除`map`的元素:
```Go
// 初始化一个字典
rating := map[string]float32{"C":5, "Go":4.5, "Python":4.5, "C++":2 }
// map有两个返回值第二个返回值如果不存在key那么ok为false如果存在ok为true
@@ -442,15 +476,16 @@ slice有一些简便的操作
delete(rating, "C") // 删除key为C的元素
```
上面说过了,`map`也是一种引用类型,如果两个`map`同时指向一个底层,那么一个改变,另一个也相应的改变:
```Go
m := make(map[string]string)
m["Hello"] = "Bonjour"
m1 := m
m1["Hello"] = "Salut" // 现在m["hello"]的值已经是Salut了
```
### make、new操作
`make`用于内建类型(`map`、`slice` 和`channel`)的内存分配。`new`用于各种类型的内存分配。
@@ -473,6 +508,7 @@ slice有一些简便的操作
## 零值
关于“零值”所指并非是空值而是一种“变量未填充前”的默认值通常为0。
此处罗列 部分类型 的 “零值”
```Go
int 0
int8 0
@@ -486,6 +522,7 @@ slice有一些简便的操作
bool false
string ""
```
## links
* [目录](<preface.md>)
* 上一章: [你好,Go](<02.1.md>)

View File

@@ -6,14 +6,16 @@
`if`也许是各种编程语言中最常见的了,它的语法概括起来就是:如果满足条件就做某事,否则做另一件事。
Go里面`if`条件判断语句中不需要括号,如下代码所示
```Go
if x > 10 {
fmt.Println("x is greater than 10")
} else {
fmt.Println("x is less than 10")
}
```
Go的`if`还有一个强大的地方就是条件判断语句里面允许声明一个变量,这个变量的作用域只能在该条件逻辑块内,其他地方就不起作用了,如下所示
```Go
// 计算获取值x,然后根据x返回的大小判断是否大于10。
if x := computedValue(); x > 10 {
@@ -24,8 +26,9 @@ Go的`if`还有一个强大的地方就是条件判断语句里面允许声明
//这个地方如果这样调用就编译出错了因为x是条件里面的变量
fmt.Println(x)
```
多个条件的时候如下所示:
```Go
if integer == 3 {
fmt.Println("The integer is equal to 3")
@@ -34,10 +37,11 @@ Go的`if`还有一个强大的地方就是条件判断语句里面允许声明
} else {
fmt.Println("The integer is greater than 3")
}
```
### goto
Go有`goto`语句——请明智地使用它。用`goto`跳转到必须在当前函数内定义的标签。例如假设这样一个循环:
```Go
func myFunc() {
i := 0
@@ -46,21 +50,24 @@ Go有`goto`语句——请明智地使用它。用`goto`跳转到必须在当前
i++
goto Here //跳转到Here去
}
```
>标签名是大小写敏感的。
### for
Go里面最强大的一个控制逻辑就是`for`,它即可以用来循环读取数据,又可以当作`while`来控制逻辑,还能迭代操作。它的语法如下:
```Go
for expression1; expression2; expression3 {
//...
}
```
`expression1``expression2``expression3`都是表达式,其中`expression1``expression3`是变量声明或者函数调用返回值之类的,`expression2`是用来条件判断,`expression1`在循环开始之前调用,`expression3`在每轮循环结束之时调用。
一个例子比上面讲那么多更有用,那么我们看看下面的例子吧:
```Go
package main
import "fmt"
func main(){
@@ -71,25 +78,28 @@ Go里面最强大的一个控制逻辑就是`for`,它即可以用来循环读
fmt.Println("sum is equal to ", sum)
}
// 输出sum is equal to 45
```
有些时候需要进行多个赋值操作由于Go里面没有`,`操作符,那么可以使用平行赋值`i, j = i+1, j-1`
有些时候如果我们忽略`expression1``expression3`
```Go
sum := 1
for ; sum < 1000; {
sum += sum
}
```
其中`;`也可以省略,那么就变成如下的代码了,是不是似曾相识?对,这就是`while`的功能。
```Go
sum := 1
for sum < 1000 {
sum += sum
}
```
在循环里面有两个关键操作`break``continue` ,`break`操作是跳出当前循环,`continue`是跳过本次循环。当嵌套过深的时候,`break`可以配合标签使用,即跳转至标签所指定的位置,详细参考如下例子:
```Go
for index := 10; index>0; index-- {
if index == 5{
@@ -99,26 +109,29 @@ Go里面最强大的一个控制逻辑就是`for`,它即可以用来循环读
}
// break打印出来10、9、8、7、6
// continue打印出来10、9、8、7、6、4、3、2、1
```
`break``continue`还可以跟着标号,用来跳到多重循环中的外层循环
`for`配合`range`可以用于读取`slice``map`的数据:
```Go
for k,v:=range map {
fmt.Println("map's key:",k)
fmt.Println("map's val:",v)
}
```
由于 Go 支持 “多值返回”, 而对于“声明而未被调用”的变量, 编译器会报错, 在这种情况下, 可以使用`_`来丢弃不需要的返回值
例如
```Go
for _, v := range map{
fmt.Println("map's val:", v)
}
```
### switch
有些时候你需要写很多的`if-else`来实现一些逻辑处理,这个时候代码看上去就很丑很冗长,而且也不易于以后的维护,这个时候`switch`就能很好的解决这个问题。它的语法如下
```Go
switch sExpr {
case expr1:
@@ -130,8 +143,9 @@ Go里面最强大的一个控制逻辑就是`for`,它即可以用来循环读
default:
other code
}
```
`sExpr``expr1``expr2``expr3`的类型必须一致。Go的`switch`非常灵活,表达式不必是常量或整数,执行的过程从上至下,直到找到匹配项;而如果`switch`没有表达式,它会匹配`true`
```Go
i := 10
switch i {
@@ -144,8 +158,9 @@ Go里面最强大的一个控制逻辑就是`for`,它即可以用来循环读
default:
fmt.Println("All I know is that i is an integer")
}
```
在第5行中我们把很多值聚合在了一个`case`里面同时Go里面`switch`默认相当于每个`case`最后带有`break`匹配成功后不会自动向下执行其他case而是跳出整个`switch`, 但是可以使用`fallthrough`强制执行后面的case代码。
```Go
integer := 6
switch integer {
@@ -167,24 +182,26 @@ Go里面最强大的一个控制逻辑就是`for`,它即可以用来循环读
default:
fmt.Println("default case")
}
```
上面的程序将输出
```Go
The integer was <= 6
The integer was <= 7
The integer was <= 8
default case
```
## 函数
函数是Go里面的核心设计它通过关键字`func`来声明,它的格式如下:
```Go
func funcName(input1 type1, input2 type2) (output1 type1, output2 type2) {
//这里是处理逻辑代码
//返回多个值
return value1, value2
}
```
上面的代码我们看出
- 关键字`func`用来声明一个函数`funcName`
@@ -196,8 +213,10 @@ Go里面最强大的一个控制逻辑就是`for`,它即可以用来循环读
- 如果有返回值, 那么必须在函数的外层添加return语句
下面我们来看一个实际应用函数的例子用来计算Max值
```Go
package main
import "fmt"
// 返回a、b中最大值.
@@ -220,15 +239,17 @@ Go里面最强大的一个控制逻辑就是`for`,它即可以用来循环读
fmt.Printf("max(%d, %d) = %d\n", x, z, max_xz)
fmt.Printf("max(%d, %d) = %d\n", y, z, max(y,z)) // 也可在这直接调用它
}
```
上面这个里面我们可以看到`max`函数有两个参数,它们的类型都是`int`,那么第一个变量的类型可以省略(即 a,b int,而非 a int, b int)默认为离它最近的类型同理多于2个同类型的变量或者返回值。同时我们注意到它的返回值就是一个类型这个就是省略写法。
### 多个返回值
Go语言比C更先进的特性其中一点就是函数能够返回多个值。
我们直接上代码看例子
```Go
package main
import "fmt"
//返回 A+B 和 A*B
@@ -245,31 +266,37 @@ Go语言比C更先进的特性其中一点就是函数能够返回多个值
fmt.Printf("%d + %d = %d\n", x, y, xPLUSy)
fmt.Printf("%d * %d = %d\n", x, y, xTIMESy)
}
```
上面的例子我们可以看到直接返回了两个参数,当然我们也可以命名返回参数的变量,这个例子里面只是用了两个类型,我们也可以改成如下这样的定义,然后返回的时候不用带上变量名,因为直接在函数里面初始化了。但如果你的函数是导出的(首字母大写),官方建议:最好命名返回值,因为不命名返回值,虽然使得代码更加简洁了,但是会造成生成的文档可读性差。
```Go
func SumAndProduct(A, B int) (add int, Multiplied int) {
add = A+B
Multiplied = A*B
return
}
```
### 变参
Go函数支持变参。接受变参的函数是有着不定数量的参数的。为了做到这点首先需要定义函数使其接受变参
```Go
func myfunc(arg ...int) {}
```
`arg ...int`告诉Go这个函数接受不定数量的参数。注意这些参数的类型全部是`int`。在函数体中,变量`arg`是一个`int``slice`
```Go
for _, n := range arg {
fmt.Printf("And the number is: %d\n", n)
}
```
### 传值与传指针
当我们传一个参数值到被调用函数里面时实际上是传了这个值的一份copy当在被调用函数中修改参数值的时候调用函数中相应实参不会发生任何变化因为数值变化只作用在copy上。
为了验证我们上面的说法,我们来看一个例子
```Go
package main
import "fmt"
//简单的一个函数,实现了参数+1的操作
@@ -288,7 +315,7 @@ Go函数支持变参。接受变参的函数是有着不定数量的参数的。
fmt.Println("x+1 = ", x1) // 应该输出"x+1 = 4"
fmt.Println("x = ", x) // 应该输出"x = 3"
}
```
看到了吗?虽然我们调用了`add1`函数,并且在`add1`中执行`a = a+1`操作,但是上面例子中`x`变量的值没有发生变化
理由很简单:因为当我们调用`add1`的时候,`add1`接收的参数其实是`x`的copy而不是`x`本身。
@@ -296,8 +323,10 @@ Go函数支持变参。接受变参的函数是有着不定数量的参数的。
那你也许会问了,如果真的需要传这个`x`本身,该怎么办呢?
这就牵扯到了所谓的指针。我们知道,变量在内存中是存放于一定地址上的,修改变量实际是修改变量地址处的内存。只有`add1`函数知道`x`变量所在的地址,才能修改`x`变量的值。所以我们需要将`x`所在地址`&x`传入函数,并将函数的参数的类型由`int`改为`*int`,即改为指针类型,才能在函数中修改`x`变量的值。此时参数仍然是按copy传递的只是copy的是一个指针。请看下面的例子
```Go
package main
import "fmt"
//简单的一个函数,实现了参数+1的操作
@@ -316,7 +345,7 @@ Go函数支持变参。接受变参的函数是有着不定数量的参数的。
fmt.Println("x+1 = ", x1) // 应该输出 "x+1 = 4"
fmt.Println("x = ", x) // 应该输出 "x = 4"
}
```
这样,我们就达到了修改`x`的目的。那么到底传指针有什么好处呢?
- 传指针使得多个函数能操作同一个对象。
@@ -325,6 +354,7 @@ Go函数支持变参。接受变参的函数是有着不定数量的参数的。
### defer
Go语言中有种不错的设计即延迟defer语句你可以在函数中添加多个defer语句。当函数执行到最后时这些defer语句会按照逆序执行最后该函数返回。特别是当你在进行一些打开资源的操作时遇到错误需要提前返回在返回前你需要关闭相应的资源不然很容易造成资源泄露等问题。如下代码所示我们一般写打开一个资源是这样操作的
```Go
func ReadWrite() bool {
file.Open("file")
@@ -342,8 +372,9 @@ Go语言中有种不错的设计即延迟defer语句你可以在函
file.Close()
return true
}
```
我们看到上面有很多重复的代码Go的`defer`有效解决了这个问题。使用它后,不但代码量减少了很多,而且程序变得更优雅。在`defer`后指定的函数会在函数退出前调用。
```Go
func ReadWrite() bool {
file.Open("file")
@@ -356,13 +387,14 @@ Go语言中有种不错的设计即延迟defer语句你可以在函
}
return true
}
```
如果有很多调用`defer`,那么`defer`是采用后进先出模式,所以如下代码会输出`4 3 2 1 0`
```Go
for i := 0; i < 5; i++ {
defer fmt.Printf("%d ", i)
}
```
### 函数作为值、类型
在Go中函数也是一种变量我们可以通过`type`来定义它,它的类型就是所有拥有相同的参数,相同的返回值的一种类型
@@ -370,8 +402,10 @@ Go语言中有种不错的设计即延迟defer语句你可以在函
type typeName func(input1 inputType1 , input2 inputType2 [, ...]) (result1 resultType1 [, ...])
函数作为类型到底有什么好处呢?那就是可以把这个类型的函数当做值来传递,请看下面的例子
```Go
package main
import "fmt"
type testInt func(int) bool // 声明了一个函数类型
@@ -410,7 +444,7 @@ Go语言中有种不错的设计即延迟defer语句你可以在函
even := filter(slice, isEven) // 函数当做值来传递了
fmt.Println("Even elements of slice are: ", even)
}
```
函数当做值和类型在我们写一些通用接口的时候非常有用,通过上面例子我们看到`testInt`这个类型是一个函数类型,然后两个`filter`函数的参数和返回值与`testInt`类型是一样的,但是我们可以实现很多种的逻辑,这样使得我们的程序变得非常的灵活。
### Panic和Recover
@@ -424,6 +458,7 @@ Recover
>是一个内建的函数,可以让进入令人恐慌的流程中的`goroutine`恢复过来。`recover`仅在延迟函数中有效。在正常的执行过程中,调用`recover`会返回`nil`,并且没有其它任何效果。如果当前的`goroutine`陷入恐慌,调用`recover`可以捕获到`panic`的输入值,并且恢复正常的执行。
下面这个函数演示了如何在过程中使用`panic`
```Go
var user = os.Getenv("USER")
@@ -432,8 +467,9 @@ Recover
panic("no value for $USER")
}
}
```
下面这个函数检查作为其参数的函数在执行时是否会产生`panic`
```Go
func throwsPanic(f func()) (b bool) {
defer func() {
@@ -444,7 +480,7 @@ Recover
f() //执行函数f如果f中出现了panic那么就可以恢复回来
return
}
```
### `main`函数和`init`函数
Go里面有两个保留的函数`init`函数(能够应用于所有的`package`)和`main`函数(只能应用于`package main`)。这两个函数在定义时不能有任何的参数和返回值。虽然一个`package`里面可以写任意多个`init`函数,但这无论是对于可读性还是以后的可维护性来说,我们都强烈建议用户在一个`package`中每个文件只写一个`init`函数。
@@ -459,15 +495,17 @@ Go程序会自动调用`init()`和`main()`,所以你不需要在任何地方
### import
我们在写Go代码的时候经常用到import这个命令用来导入包文件而我们经常看到的方式参考如下
```Go
import(
"fmt"
)
```
然后我们代码里面可以通过如下的方式调用
```Go
fmt.Println("hello world")
```
上面这个fmt是Go语言的标准库其实是去`GOROOT`环境变量指定目录下去加载该模块当然Go的import还支持如下两种方式来加载自己写的模块
1. 相对路径
@@ -505,12 +543,13 @@ Go程序会自动调用`init()`和`main()`,所以你不需要在任何地方
3. _操作
这个操作经常是让很多人费解的一个操作符请看下面这个import
```Go
import (
"database/sql"
_ "github.com/ziutek/mymysql/godrv"
)
```
_操作其实是引入该包而不直接使用包里面的函数而是调用了该包里面的init函数。

View File

@@ -1,16 +1,19 @@
# 2.4 struct类型
## struct
Go语言中也和C或者其他语言一样我们可以声明新的类型作为其它类型的属性或字段的容器。例如我们可以创建一个自定义类型`person`代表一个人的实体。这个实体拥有属性:姓名和年龄。这样的类型我们称之`struct`。如下代码所示:
```Go
type person struct {
name string
age int
}
```
看到了吗声明一个struct如此简单上面的类型包含有两个字段
- 一个string类型的字段name用来保存用户名称这个属性
- 一个int类型的字段age,用来保存用户年龄这个属性
如何使用struct呢请看下面的代码
```Go
type person struct {
name string
@@ -22,6 +25,7 @@ Go语言中也和C或者其他语言一样我们可以声明新的类型
P.name = "Astaxie" // 赋值"Astaxie"给P的name属性.
P.age = 25 // 赋值"25"给变量P的age属性
fmt.Printf("The person's name is %s", P.name) // 访问P的name属性.
```
除了上面这种P的声明使用之外还有另外几种声明使用方式
- 1.按照顺序提供初始化值
@@ -37,8 +41,10 @@ Go语言中也和C或者其他语言一样我们可以声明新的类型
P := new(person)
下面我们看一个完整的使用struct的例子
```Go
package main
import "fmt"
// 声明一个新的类型
@@ -81,15 +87,17 @@ Go语言中也和C或者其他语言一样我们可以声明新的类型
fmt.Printf("Of %s and %s, %s is older by %d years\n",
bob.name, paul.name, bp_Older.name, bp_diff)
}
```
### struct的匿名字段
我们上面介绍了如何定义一个struct定义的时候是字段名与其类型一一对应实际上Go支持只提供类型而不写字段名的方式也就是匿名字段也称为嵌入字段。
当匿名字段是一个struct的时候那么这个struct所拥有的全部字段都被隐式地引入了当前定义的这个struct。
让我们来看一个例子,让上面说的这些更具体化
```Go
package main
import "fmt"
type Human struct {
@@ -125,7 +133,7 @@ Go语言中也和C或者其他语言一样我们可以声明新的类型
mark.weight += 60
fmt.Println("His weight is", mark.weight)
}
```
图例如下:
![](images/2.4.student_struct.png?raw=true)
@@ -133,13 +141,16 @@ Go语言中也和C或者其他语言一样我们可以声明新的类型
图2.7 struct组合Student组合了Human struct和string基本类型
我们看到Student访问属性age和name的时候就像访问自己所有用的字段一样匿名字段就是这样能够实现字段的继承。是不是很酷啊还有比这个更酷的呢那就是student还能访问Human这个字段作为字段名。请看下面的代码是不是更酷了。
```Go
mark.Human = Human{"Marcus", 55, 220}
mark.Human.age -= 1
```
通过匿名访问和修改字段相当的有用但是不仅仅是struct字段哦所有的内置类型和自定义类型都是可以作为匿名字段的。请看下面的例子
```Go
package main
import "fmt"
type Skills []string
@@ -175,7 +186,7 @@ Go语言中也和C或者其他语言一样我们可以声明新的类型
jane.int = 3
fmt.Println("Her preferred number is", jane.int)
}
```
从上面例子我们看出来struct不仅仅能够将struct作为匿名字段、自定义类型、内置类型都可以作为匿名字段而且可以在相应的字段上面进行函数操作如例子中的append
这里有一个问题如果human里面有一个字段叫做phone而student也有一个字段叫做phone那么该怎么办呢
@@ -183,8 +194,10 @@ Go语言中也和C或者其他语言一样我们可以声明新的类型
Go里面很简单的解决了这个问题最外层的优先访问也就是当你通过`student.phone`访问的时候是访问student里面的字段而不是human里面的字段。
这样就允许我们去重载通过匿名字段继承的一些字段,当然如果我们想访问重载后对应匿名类型里面的字段,可以通过匿名字段名来访问。请看下面的例子
```Go
package main
import "fmt"
type Human struct {
@@ -205,7 +218,7 @@ Go里面很简单的解决了这个问题最外层的优先访问也就是
// 如果我们要访问Human的phone字段
fmt.Println("Bob's personal phone is:", Bob.Human.phone)
}
```
## links
* [目录](<preface.md>)

View File

@@ -3,8 +3,10 @@
## method
现在假设有这么一个场景你定义了一个struct叫做长方形你现在想要计算他的面积那么按照我们一般的思路应该会用下面的方式来实现
```Go
package main
import "fmt"
type Rectangle struct {
@@ -21,7 +23,7 @@
fmt.Println("Area of r1 is: ", area(r1))
fmt.Println("Area of r2 is: ", area(r2))
}
```
这段代码可以计算出来长方形的面积但是area()不是作为Rectangle的方法实现的类似面向对象里面的方法而是将Rectangle的对象如r1,r2作为参数传入函数计算面积的。
这样实现当然没有问题咯,但是当需要增加圆形、正方形、五边形甚至其它多边形的时候,你想计算他们的面积的时候怎么办啊?那就只能增加新的函数咯,但是函数名你就必须要跟着换了,变成`area_rectangle, area_circle, area_triangle...`
@@ -49,8 +51,10 @@ method的语法如下
func (r ReceiverType) funcName(parameters) (results)
下面我们用最开始的例子用method来实现
```Go
package main
import (
"fmt"
"math"
@@ -85,7 +89,7 @@ method的语法如下
fmt.Println("Area of c2 is: ", c2.area())
}
```
在使用method的时候重要注意几点
@@ -104,10 +108,12 @@ method的语法如下
>值得说明的一点是图示中method用虚线标出意思是此处方法的Receiver是以值传递而非引用传递是的Receiver还可以是指针, 两者的差别在于, 指针作为Receiver会对实例对象的内容发生操作,而普通类型作为Receiver仅仅是以副本作为操作对象,并不对原实例对象发生操作。后文对此会有详细论述。
那是不是method只能作用在struct上面呢当然不是咯他可以定义在任何你自定义的类型、内置类型、struct等各种类型上面。这里你是不是有点迷糊了什么叫自定义类型自定义类型不就是struct嘛不是这样的哦struct只是自定义类型里面一种比较特殊的类型而已还有其他自定义类型申明可以通过如下这样的申明来实现。
```Go
type typeName typeLiteral
```
请看下面这个申明自定义类型的代码
```Go
type ages int
@@ -121,14 +127,16 @@ method的语法如下
...
"December":31,
}
```
看到了吗?简单的很吧,这样你就可以在自己的代码里面定义有意义的类型了,实际上只是一个定义了一个别名,有点类似于c中的typedef例如上面ages替代了int
好了,让我们回到`method`
你可以在任何的自定义类型中定义任意多的`method`,接下来让我们看一个复杂一点的例子
```Go
package main
import "fmt"
const(
@@ -200,7 +208,7 @@ method的语法如下
fmt.Println("Obviously, now, the biggest one is", boxes.BiggestColor().String())
}
```
上面的代码通过const定义了一些常量然后定义了一些自定义类型
- Color作为byte的别名
@@ -242,8 +250,10 @@ method的语法如下
### method继承
前面一章我们学习了字段的继承那么你也会发现Go的一个神奇之处method也是可以继承的。如果匿名字段实现了一个method那么包含这个匿名字段的struct也能调用该method。让我们来看下面这个例子
```Go
package main
import "fmt"
type Human struct {
@@ -274,11 +284,13 @@ method的语法如下
mark.SayHi()
sam.SayHi()
}
```
### method重写
上面的例子中如果Employee想要实现自己的SayHi,怎么办简单和匿名字段冲突一样的道理我们可以在Employee上面定义一个method重写了匿名字段的方法。请看下面的例子
```Go
package main
import "fmt"
type Human struct {
@@ -315,7 +327,7 @@ method的语法如下
mark.SayHi()
sam.SayHi()
}
```
上面的代码设计的是如此的美妙让人不自觉的为Go的设计惊叹
通过这些内容我们可以设计出基本的面向对象的程序了但是Go里面的面向对象是如此的简单没有任何的私有、公有关键字通过大小写来实现(大写开头的为公有,小写开头的为私有),方法也同样适用这个原则。

View File

@@ -14,6 +14,7 @@ Go语言里面设计最精妙的应该算interface它让面向对象内容
上面这些方法的组合称为interface(被对象Student和Employee实现)。例如Student和Employee都实现了interfaceSayHi和Sing也就是这两个对象是该interface类型。而Employee没有实现这个interfaceSayHi、Sing和BorrowMoney因为Employee没有实现BorrowMoney这个方法。
### interface类型
interface类型定义了一组方法如果某个对象实现了某个接口的所有方法则此对象就实现了此接口。详细的语法参考下面这个例子
```Go
type Human struct {
name string
@@ -82,7 +83,7 @@ interface类型定义了一组方法如果某个对象实现了某个接口
Sing(song string)
SpendSalary(amount float32)
}
```
通过上面的代码我们可以知道interface可以被任意的对象实现。我们看到上面的Men interface被Human、Student和Employee实现。同理一个对象可以实现任意多个interface例如上面的Student实现了Men和YoungChap两个interface。
最后任意的类型都实现了空interface(我们这样定义interface{})也就是包含0个method的interface。
@@ -93,8 +94,10 @@ interface类型定义了一组方法如果某个对象实现了某个接口
因为m能够持有这三种类型的对象所以我们可以定义一个包含Men类型元素的slice这个slice可以被赋予实现了Men接口的任意结构的对象这个和我们传统意义上面的slice有所不同。
让我们来看一下下面这个例子:
```Go
package main
import "fmt"
type Human struct {
@@ -169,11 +172,12 @@ interface类型定义了一组方法如果某个对象实现了某个接口
value.SayHi()
}
}
```
通过上面的代码你会发现interface就是一组抽象方法的集合它必须由其他非interface类型实现而不能自我实现 Go通过interface实现了duck-typing:即"当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子"。
### 空interface
空interface(interface{})不包含任何的method正因为如此所有的类型都实现了空interface。空interface对于描述起不到任何的作用(因为它不包含任何的method但是空interface在我们需要存储任意类型的数值的时候相当有用因为它可以存储任意类型的数值。它有点类似于C语言的void*类型。
```Go
// 定义a为空接口
var a interface{}
@@ -182,17 +186,20 @@ interface类型定义了一组方法如果某个对象实现了某个接口
// a可以存储任意类型的数值
a = i
a = s
```
一个函数把interface{}作为参数那么他可以接受任意类型的值作为参数如果一个函数返回interface{},那么也就可以返回任意类型的值。是不是很有用啊!
### interface函数参数
interface的变量可以持有任意实现该interface类型的对象这给我们编写函数(包括method)提供了一些额外的思考我们是不是可以通过定义interface参数让函数接受各种类型的参数。
举个例子fmt.Println是我们常用的一个函数但是你是否注意到它可以接受任意类型的数据。打开fmt的源码文件你会看到这样一个定义:
```Go
type Stringer interface {
String() string
}
```
也就是说任何实现了String方法的类型都能作为参数被fmt.Println调用,让我们来试一试
```Go
package main
import (
@@ -215,12 +222,14 @@ interface的变量可以持有任意实现该interface类型的对象这给
Bob := Human{"Bob", 39, "000-7777-XXX"}
fmt.Println("This Human is : ", Bob)
}
```
现在我们再回顾一下前面的Box示例你会发现Color结构也定义了一个methodString。其实这也是实现了fmt.Stringer这个interface即如果需要某个类型能被fmt包以特殊的格式输出你就必须实现Stringer这个接口。如果没有实现这个接口fmt将以默认的方式输出。
```Go
//实现同样的功能
fmt.Println("The biggest one is", boxes.BiggestsColor().String())
fmt.Println("The biggest one is", boxes.BiggestsColor())
```
实现了error接口的对象即实现了Error() string的对象使用fmt输出时会调用Error()方法因此不必再定义String()方法了。
### interface变量存储的类型
@@ -233,6 +242,7 @@ interface的变量可以持有任意实现该interface类型的对象这给
如果element里面确实存储了T类型的数值那么ok返回true否则返回false。
让我们通过一个例子来更加深入的理解。
```Go
package main
@@ -272,13 +282,14 @@ interface的变量可以持有任意实现该interface类型的对象这给
}
}
}
```
是不是很简单啊同时你是否注意到了多个if里面还记得我前面介绍流程时讲过if里面允许初始化变量。
也许你注意到了我们断言的类型越多那么if else也就越多所以才引出了下面要介绍的switch。
- switch测试
最好的讲解就是代码例子,现在让我们重写上面的这个实现
```Go
package main
@@ -319,21 +330,23 @@ interface的变量可以持有任意实现该interface类型的对象这给
}
}
}
```
这里有一点需要强调的是:`element.(type)`语法不能在switch外的任何逻辑里面使用如果你要在switch外面判断一个类型就使用`comma-ok`
### 嵌入interface
Go里面真正吸引人的是它内置的逻辑语法就像我们在学习Struct时学习的匿名字段多么的优雅啊那么相同的逻辑引入到interface里面那不是更加完美了。如果一个interface1作为interface2的一个嵌入字段那么interface2隐式的包含了interface1里面的method。
我们可以看到源码包container/heap里面有这样的一个定义
```Go
type Interface interface {
sort.Interface //嵌入字段sort.Interface
Push(x interface{}) //a Push method to push elements into the heap
Pop() interface{} //a Pop elements that pops elements from the heap
}
```
我们看到sort.Interface其实就是嵌入字段把sort.Interface的所有method给隐式的包含进来了。也就是下面三个方法
```Go
type Interface interface {
// Len is the number of elements in the collection.
@@ -344,49 +357,55 @@ Go里面真正吸引人的是它内置的逻辑语法就像我们在学习Str
// Swap swaps the elements with indexes i and j.
Swap(i, j int)
}
```
另一个例子就是io包下面的 io.ReadWriter 它包含了io包下面的Reader和Writer两个interface
```Go
// io.ReadWriter
type ReadWriter interface {
Reader
Writer
}
```
### 反射
Go语言实现了反射所谓反射就是能检查程序在运行时的状态。我们一般用到的包是reflect包。如何运用reflect包官方的这篇文章详细的讲解了reflect包的实现原理[laws of reflection](http://golang.org/doc/articles/laws_of_reflection.html)
使用reflect一般分成三步下面简要的讲解一下要去反射是一个类型的值(这些值都实现了空interface)首先需要把它转化成reflect对象(reflect.Type或者reflect.Value根据不同的情况调用不同的函数)。这两种获取方式如下:
```Go
t := reflect.TypeOf(i) //得到类型的元数据,通过t我们能获取类型定义里面的所有元素
v := reflect.ValueOf(i) //得到实际的值通过v我们获取存储在里面的值还可以去改变值
```
转化为reflect对象之后我们就可以进行一些操作了也就是将reflect对象转化成相应的值例如
```Go
tag := t.Elem().Field(0).Tag //获取定义在struct里面的标签
name := v.Elem().Field(0).String() //获取存储在第一个字段里面的值
```
获取反射值能返回相应的类型和数值
```Go
var x float64 = 3.4
v := reflect.ValueOf(x)
fmt.Println("type:", v.Type())
fmt.Println("kind is float64:", v.Kind() == reflect.Float64)
fmt.Println("value:", v.Float())
```
最后,反射的话,那么反射的字段必须是可修改的,我们前面学习过传值和传引用,这个里面也是一样的道理。反射的字段必须是可读写的意思是,如果下面这样写,那么会发生错误
```Go
var x float64 = 3.4
v := reflect.ValueOf(x)
v.SetFloat(7.1)
```
如果要修改相应的值,必须这样写
```Go
var x float64 = 3.4
p := reflect.ValueOf(&x)
v := p.Elem()
v.SetFloat(7.1)
```
上面只是对反射的简单介绍,更深入的理解还需要自己在编程中不断的实践。
## links

View File

@@ -7,10 +7,12 @@
goroutine是Go并行设计的核心。goroutine说到底其实就是线程但是它比线程更小十几个goroutine可能体现在底层就是五六个线程Go语言内部帮你实现了这些goroutine之间的内存共享。执行goroutine只需极少的栈内存(大概是4~5KB)当然会根据相应的数据伸缩。也正因为如此可同时运行成千上万个并发任务。goroutine比thread更易用、更高效、更轻便。
goroutine是通过Go的runtime管理的一个线程管理器。goroutine通过`go`关键字实现了,其实就是一个普通的函数。
```Go
go hello(a, b, c)
```
通过关键字go就启动了一个goroutine。我们来看一个例子
```Go
package main
@@ -41,7 +43,7 @@ goroutine是通过Go的runtime管理的一个线程管理器。goroutine通过`g
// hello
// world
// hello
```
我们可以看到go关键字很方便的就实现了并发编程。
上面的多个goroutine运行在同一个进程里面共享内存数据不过设计上我们要遵循不要通过共享来通信而要通过通信来共享。
@@ -51,17 +53,20 @@ goroutine是通过Go的runtime管理的一个线程管理器。goroutine通过`g
## channels
goroutine运行在相同的地址空间因此访问共享内存必须做好同步。那么goroutine之间如何进行数据的通信呢Go提供了一个很好的通信机制channel。channel可以与Unix shell 中的双向管道做类比可以通过它发送或者接收值。这些值只能是特定的类型channel类型。定义一个channel时也需要定义发送到channel的值的类型。注意必须使用make 创建channel
```Go
ci := make(chan int)
cs := make(chan string)
cf := make(chan interface{})
```
channel通过操作符`<-`来接收和发送数据
```Go
ch <- v // 发送v到channel ch.
v := <-ch // 从ch中接收数据并赋值给v
```
我们把这些应用到我们的例子中来:
```Go
package main
@@ -85,18 +90,19 @@ channel通过操作符`<-`来接收和发送数据
fmt.Println(x, y, x + y)
}
```
默认情况下channel接收和发送数据都是阻塞的除非另一端已经准备好这样就使得Goroutines同步变的更加的简单而不需要显式的lock。所谓阻塞也就是如果读取value := <-ch它将会被阻塞直到有数据接收。其次任何发送ch<-5将会被阻塞直到数据被读出。无缓冲channel是在多个goroutine之间同步很棒的工具。
## Buffered Channels
上面我们介绍了默认的非缓存类型的channel不过Go也允许指定channel的缓冲大小很简单就是channel可以存储多少元素。ch:= make(chan bool, 4)创建了可以存储4个元素的bool 型channel。在这个channel 中前4个元素可以无阻塞的写入。当写入第5个元素时代码将会阻塞直到其他goroutine从channel 中读取一些元素,腾出空间。
```Go
ch := make(chan type, value)
```
当 value = 0 时channel 是无缓冲阻塞读写的当value > 0 时channel 有缓冲、是非阻塞的,直到写满 value 个元素才阻塞写入。
我们看一下下面这个例子你可以在自己本机测试一下修改相应的value值
```Go
package main
@@ -111,9 +117,10 @@ channel通过操作符`<-`来接收和发送数据
}
//修改为1报如下的错误:
//fatal error: all goroutines are asleep - deadlock!
```
## Range和Close
上面这个例子中我们需要读取两次c这样不是很方便Go考虑到了这一点所以也可以通过range像操作slice或者map一样操作缓存类型的channel请看下面的例子
```Go
package main
@@ -137,7 +144,7 @@ channel通过操作符`<-`来接收和发送数据
fmt.Println(i)
}
}
```
`for i := range c`能够不断的读取channel里面的数据直到该channel被显式的关闭。上面代码我们看到可以显式的关闭channel生产者通过内置函数`close`关闭channel。关闭channel之后就无法再发送任何数据了在消费方可以通过语法`v, ok := <-ch`测试channel是否被关闭。如果ok返回false那么说明channel已经没有任何数据并且已经被关闭。
>记住应该在生产者的地方关闭channel而不是消费的地方去关闭它这样容易引起panic
@@ -148,6 +155,7 @@ channel通过操作符`<-`来接收和发送数据
我们上面介绍的都是只有一个channel的情况那么如果存在多个channel的时候我们该如何操作呢Go里面提供了一个关键字`select`,通过`select`可以监听channel上的数据流动。
`select`默认是阻塞的只有当监听的channel中有发送或接收可以进行时才会运行当多个channel都准备好的时候select是随机的选择一个执行的。
```Go
package main
@@ -177,8 +185,9 @@ channel通过操作符`<-`来接收和发送数据
}()
fibonacci(c, quit)
}
```
`select`里面还有default语法`select`其实就是类似switch的功能default就是当监听的channel都没有准备好的时候默认执行的select不再阻塞等待channel
```Go
select {
case i := <-c:
@@ -186,9 +195,10 @@ channel通过操作符`<-`来接收和发送数据
default:
// 当c阻塞的时候执行这里
}
```
## 超时
有时候会出现goroutine阻塞的情况那么我们如何避免整个程序进入阻塞的情况呢我们可以利用select来设置超时通过如下的方式实现
```Go
func main() {
c := make(chan int)
@@ -207,7 +217,7 @@ channel通过操作符`<-`来接收和发送数据
}()
<- o
}
```
## runtime goroutine
runtime包中有几个处理goroutine的函数

View File

@@ -1,13 +1,14 @@
# 2.8 总结
这一章我们主要介绍了Go语言的一些语法通过语法我们可以发现Go是多么的简单只有二十五个关键字。让我们再来回顾一下这些关键字都是用来干什么的。
```Go
break default func interface select
case defer go map struct
chan else goto package switch
const fallthrough if range type
continue for import return var
```
- var和const参考2.2Go语言基础里面的变量和常量申明
- package和import已经有过短暂的接触
- func 用于定义函数和方法

View File

@@ -23,7 +23,7 @@ Go中支持MySQL的驱动目前比较多有如下几种有些是支持data
`departname` VARCHAR(64) NULL DEFAULT NULL,
`created` DATE NULL DEFAULT NULL,
PRIMARY KEY (`uid`)
)
);
CREATE TABLE `userdetail` (
`uid` INT(10) NOT NULL DEFAULT '0',
@@ -33,6 +33,7 @@ Go中支持MySQL的驱动目前比较多有如下几种有些是支持data
)
如下示例将示范如何使用database/sql接口对数据库表进行增删改查操作
```Go
package main
@@ -108,7 +109,8 @@ Go中支持MySQL的驱动目前比较多有如下几种有些是支持data
panic(err)
}
}
```
通过上面的代码我们可以看出Go操作Mysql数据库是很方便的。

View File

@@ -29,6 +29,7 @@ Go支持sqlite的驱动也比较多但是好多都是不支持database/sql接
);
看下面Go程序是如何操作数据库表数据:增删改查
```Go
package main
@@ -104,7 +105,7 @@ Go支持sqlite的驱动也比较多但是好多都是不支持database/sql接
panic(err)
}
}
```
我们可以看到上面的代码和MySQL例子里面的代码几乎是一模一样的唯一改变的就是导入的驱动改变了然后调用`sql.Open`是采用了SQLite的方式打开。

View File

@@ -38,12 +38,14 @@ Go实现的支持PostgreSQL的驱动也很多因为国外很多人在开发
看下面这个Go如何操作数据库表数据:增删改查
package main
```Go
package main
import (
"database/sql"
"fmt"
_ "https://github.com/lib/pq"
_ "github.com/lib/pq"
)
func main() {
@@ -58,10 +60,15 @@ package main
checkErr(err)
//pg不支持这个函数因为他没有类似MySQL的自增ID
id, err := res.LastInsertId()
checkErr(err)
// id, err := res.LastInsertId()
// checkErr(err)
// fmt.Println(id)
var lastInsertId int
err = db.QueryRow("INSERT INTO userinfo(username,departname,created) VALUES($1,$2,$3) returning uid;", "astaxie", "研发部门", "2012-12-09").Scan(&lastInsertId)
checkErr(err)
fmt.Println("最后插入id =", lastInsertId)
fmt.Println(id)
//更新数据
stmt, err = db.Prepare("update userinfo set username=$1 where uid=$2")
@@ -113,6 +120,7 @@ package main
panic(err)
}
}
```
从上面的代码我们可以看到PostgreSQL是通过`$1`,`$2`这种方式来指定要传递的参数而不是MySQL中的`?`另外在sql.Open中的dsn信息的格式也与MySQL的驱动中的dsn格式不一样所以在使用时请注意它们的差异。

View File

@@ -302,8 +302,7 @@ TCP有很多连接控制函数我们平常用到比较多的有如下几个
用来设置写入/读取一个连接的超时时间。当超过设置时间时,连接自动关闭。
func (c *TCPConn) SetKeepAlive(keepalive bool) os.Error
设置客户端是否和服务器端保持长连接可以降低建立TCP连接时的握手开销对于一些需要频繁交换数据的应用场景比较适用。
设置keepAlive属性是操作系统层在tcp上没有数据和ACK的时候会间隔性的发送keepalive包操作系统可以通过该包来判断一个tcp连接是否已经断开在windows上默认2个小时没有收到数据和keepalive包的时候人为tcp连接已经断开这个功能和我们通常在应用层加的心跳包的功能类似。
更多的内容请查看`net`包的文档。
## UDP Socket

View File

@@ -90,7 +90,7 @@ Go没有为REST提供直接支持但是因为RESTful是基于HTTP协议实现
func adduser(w http.ResponseWriter, r *http.Request) {
uid := r.FormValue("uid")
fmt.Fprint(w, "you are add user %s", uid)
fmt.Fprintf(w, "you are add user %s", uid)
}
func main() {

View File

@@ -59,7 +59,7 @@
r.ParseForm()
username := r.Form.Get("username")
CleanMap := make(map[string]interface{}, 0)
if ok, _ := regexp.MatchString("^[a-zA-Z0-9].$", username); ok {
if ok, _ := regexp.MatchString("^[a-zA-Z0-9]+$", username); ok {
CleanMap["username"] = username
}