248
pt-br/02.3.md
248
pt-br/02.3.md
@@ -1,16 +1,16 @@
|
||||
# 2.3 Control statements and functions
|
||||
# 2.3 Declarações de controle e funções
|
||||
|
||||
In this section, we are going to talk about control statements and function operations in Go.
|
||||
Nesta seção nós vamos falar sobre declarações de controle e operações de funções em Go.
|
||||
|
||||
## Control statement
|
||||
## Declarações de Controle
|
||||
|
||||
The greatest invention in programming is flow control. Because of them, you are able to use simple control statements that can be used to represent complex logic. There are three categories of flow control: conditional, cycle control and unconditional jump.
|
||||
A maior invenção na programação é o controle de fluxo. Por causa dele, você é capaz de utilizar declarações de controle simples que podem ser usadas para representar lógicas complexas. Existem três categorias de controle de fluxo: condicional, controle de ciclo e salto incondicional.
|
||||
|
||||
### if
|
||||
|
||||
`if` will most likely be the most common keyword in your programs. If it meets the conditions, then it does something and it does something else if not.
|
||||
`if` provavelmente será a palavra-chave mais utilizada nos seus programas. Se ele atende as condições, então ele faz alguma coisa e caso contrário faz alguma outra coisa.
|
||||
|
||||
`if` doesn't need parentheses in Go.
|
||||
`if` não precisa de parênteses em Go.
|
||||
|
||||
if x > 10 {
|
||||
fmt.Println("x is greater than 10")
|
||||
@@ -18,19 +18,19 @@ The greatest invention in programming is flow control. Because of them, you are
|
||||
fmt.Println("x is less than or equal to 10")
|
||||
}
|
||||
|
||||
The most useful thing concerning `if` in Go is that it can have one initialization statement before the conditional statement. The scope of the variables defined in this initialization statement are only available inside the block of the defining `if`.
|
||||
A coisa mais útil sobre o `if` em Go é que ele pode ter uma instrução de inicialização antes da instrução condicional. O escopo das variáveis definidas nesta instrução de inicialização só estão disponíveis dentro do bloco de definição do `if`.
|
||||
|
||||
// initialize x, then check if x greater than
|
||||
// inicializa x e então confere se x é maior que 10
|
||||
if x := computedValue(); x > 10 {
|
||||
fmt.Println("x is greater than 10")
|
||||
} else {
|
||||
fmt.Println("x is less than 10")
|
||||
}
|
||||
|
||||
// the following code will not compile
|
||||
// o código seguinte não compilará
|
||||
fmt.Println(x)
|
||||
|
||||
Use if-else for multiple conditions.
|
||||
Use if-else para múltiplas condições.
|
||||
|
||||
if integer == 3 {
|
||||
fmt.Println("The integer is equal to 3")
|
||||
@@ -42,29 +42,29 @@ Use if-else for multiple conditions.
|
||||
|
||||
### goto
|
||||
|
||||
Go has a `goto` keyword, but be careful when you use it. `goto` reroutes the control flow to a previously defined `label` within the body of same code block.
|
||||
Go possui uma palavra-chave chamada `goto`, mas cuidado ao utilizar ela. `goto` reencaminha o fluxo de controle para um `label` definido anteriormente dentro do mesmo bloco de código.
|
||||
|
||||
func myFunc() {
|
||||
i := 0
|
||||
Here: // label ends with ":"
|
||||
Here: // label termina com ":"
|
||||
fmt.Println(i)
|
||||
i++
|
||||
goto Here // jump to label "Here"
|
||||
goto Here // pule para o label "Here"
|
||||
}
|
||||
|
||||
The label name is case sensitive.
|
||||
O nome do label é case sensitive.
|
||||
|
||||
### for
|
||||
|
||||
`for` is the most powerful control logic in Go. It can read data in loops and iterative operations, just like `while`.
|
||||
`for` é a lógica de controle mais poderosa em Go. Ele pode ler dados em loops e operações iterativas, assim como o `while`.
|
||||
|
||||
for expression1; expression2; expression3 {
|
||||
//...
|
||||
}
|
||||
|
||||
`expression1`, `expression2` and `expression3` are all expressions, where `expression1` and `expression3` are variable definitions or return values from functions, and `expression2` is a conditional statement. `expression1` will be executed once before looping, and `expression3` will be executed after each loop.
|
||||
`expression1`, `expression2` e `expression3` são todas expressões, onde `expression1` e `expression3` são definições de variáveis ou atribuições, e `expression2` é uma declaração condicional. `expression1` será executada uma vez antes do loop, e `expression3` será executada depois de cada iteração do loop.
|
||||
|
||||
Examples are more useful than words.
|
||||
Exemplos são mais úteis que palavras.
|
||||
|
||||
package main
|
||||
import "fmt"
|
||||
@@ -76,43 +76,43 @@ Examples are more useful than words.
|
||||
}
|
||||
fmt.Println("sum is equal to ", sum)
|
||||
}
|
||||
// Print:sum is equal to 45
|
||||
// Mostra: sum is equal to 45
|
||||
|
||||
Sometimes we need multiple assignments, but Go doesn't have the `,` operator, so we use parallel assignment like `i, j = i + 1, j - 1`.
|
||||
Algumas vezes nós precisamos de várias atribuições, porém Go não possui o operador `,`, então nós usamos atribuições paralelas como `i, j = i + 1, j - 1`.
|
||||
|
||||
We can omit `expression1` and `expression3` if they are not necessary.
|
||||
Nós podemos omitir as expressões `expression1` e `expression3` se elas não forem necessárias.
|
||||
|
||||
sum := 1
|
||||
for ; sum < 1000; {
|
||||
sum += sum
|
||||
}
|
||||
|
||||
Omit `;` as well. Feel familiar? Yes, it's identical to `while`.
|
||||
Podemos omitir também o `;`. Isto lhe parece familiar? Sim, é idêntico ao `while`.
|
||||
|
||||
sum := 1
|
||||
for sum < 1000 {
|
||||
sum += sum
|
||||
}
|
||||
|
||||
There are two important operations in loops which are `break` and `continue`. `break` jumps out of the loop, and `continue` skips the current loop and starts the next one. If you have nested loops, use `break` along with labels.
|
||||
Existem duas operações importantes em loops que são `break` e `continue`. `break` "pula" fora do loop, e `continue` ignora o loop atual e inicia o próximo. Se você tiver loops aninhados use `break` juntamente com labels.
|
||||
|
||||
for index := 10; index>0; index-- {
|
||||
if index == 5{
|
||||
break // or continue
|
||||
break // ou continue
|
||||
}
|
||||
fmt.Println(index)
|
||||
}
|
||||
// break prints 10、9、8、7、6
|
||||
// continue prints 10、9、8、7、6、4、3、2、1
|
||||
// break mostra 10、9、8、7、6
|
||||
// continue mostra 10、9、8、7、6、4、3、2、1
|
||||
|
||||
`for` can read data from `slice` and `map` when it is used together with `range`.
|
||||
`for` pode ler dados de um `slice` ou `map` quando for utilizado junto com `range`.
|
||||
|
||||
for k,v:=range map {
|
||||
fmt.Println("map's key:",k)
|
||||
fmt.Println("map's val:",v)
|
||||
}
|
||||
|
||||
Because Go supports multi-value returns and gives compile errors when you don't use values that were defined, you may want to use `_` to discard certain return values.
|
||||
Como Go suporta múltiplos valores de retorno e gera um erro de compilação caso você não use os valores que foram definidos, você pode querer utilizar `_` para descartar certos valores de retorno.
|
||||
|
||||
for _, v := range map{
|
||||
fmt.Println("map's val:", v)
|
||||
@@ -120,7 +120,7 @@ Because Go supports multi-value returns and gives compile errors when you don't
|
||||
|
||||
### switch
|
||||
|
||||
Sometimes you may find that you are using too many `if-else` statements to implement some logic, which may make it difficult to read and maintain in the future. This is the perfect time to use the `switch` statement to solve this problem.
|
||||
As vezes você pode achar que está usando muitas instruções `if-else` para implementar uma lógica, o que pode dificultar a leitura e manutenção no futuro. Este é o momento perfeito para utilizar a instrução `switch` para resolver este problema.
|
||||
|
||||
switch sExpr {
|
||||
case expr1:
|
||||
@@ -133,7 +133,7 @@ Sometimes you may find that you are using too many `if-else` statements to imple
|
||||
other code
|
||||
}
|
||||
|
||||
The type of `sExpr`, `expr1`, `expr2`, and `expr3` must be the same. `switch` is very flexible. Conditions don't have to be constants and it executes from top to bottom until it matches conditions. If there is no statement after the keyword `switch`, then it matches `true`.
|
||||
Os tipos de `sExpr`, `expr1`, `expr2`, e `expr3` devem ser o mesmo. `switch` é muito flexível. As condições não precisam ser constantes e são executadas de cima para baixo até que encontre uma condição válida. Se não houver nenhuma instrução após a palavra-chave `switch`, então ela corresponderá a `true`.
|
||||
|
||||
i := 10
|
||||
switch i {
|
||||
@@ -147,7 +147,7 @@ The type of `sExpr`, `expr1`, `expr2`, and `expr3` must be the same. `switch` is
|
||||
fmt.Println("All I know is that i is an integer")
|
||||
}
|
||||
|
||||
In the fifth line, we put many values in one `case`, and we don't need to add the `break` keyword at the end of `case`'s body. It will jump out of the switch body once it matched any case. If you want to continue to matching more cases, you need to use the`fallthrough` statement.
|
||||
Na quinta linha, colocamos vários valores em um `case`, e não precisamos adicionar a palavra-chave `break` no final do bloco do `case`. Ele irá sair do bloco do switch quando encontrar uma condição verdadeira. Se você deseja continuar verificando mais casos, você precisará utilizar a instrução `fallthrough`.
|
||||
|
||||
integer := 6
|
||||
switch integer {
|
||||
@@ -170,39 +170,39 @@ In the fifth line, we put many values in one `case`, and we don't need to add th
|
||||
fmt.Println("default case")
|
||||
}
|
||||
|
||||
This program prints the following information.
|
||||
Este programa mostra a seguinte informação.
|
||||
|
||||
integer <= 6
|
||||
integer <= 7
|
||||
integer <= 8
|
||||
default case
|
||||
|
||||
## Functions
|
||||
## Funções
|
||||
|
||||
Use the `func` keyword to define a function.
|
||||
Use a palavra-chave `func` para definir uma função.
|
||||
|
||||
func funcName(input1 type1, input2 type2) (output1 type1, output2 type2) {
|
||||
// function body
|
||||
// multi-value return
|
||||
// corpo da função
|
||||
// retorno de múltiplos valores
|
||||
return value1, value2
|
||||
}
|
||||
|
||||
We can extrapolate the following information from the example above.
|
||||
Nós podemos extrapolar as seguintes informações do exemplo acima.
|
||||
|
||||
- Use keyword `func` to define a function `funcName`.
|
||||
- Functions have zero, one or more than one arguments. The argument type comes after the argument name and arguments are separated by `,`.
|
||||
- Functions can return multiple values.
|
||||
- There are two return values named `output1` and `output2`, you can omit their names and use their type only.
|
||||
- If there is only one return value and you omitted the name, you don't need brackets for the return values.
|
||||
- If the function doesn't have return values, you can omit the return parameters altogether.
|
||||
- If the function has return values, you have to use the `return` statement somewhere in the body of the function.
|
||||
- Use a palavra-chave `func` para definir uma função `funcName`.
|
||||
- Funções tem zero, um ou mais argumentos. O tipo do argumento vem depois do nome do argumento e vários argumentos são separados por `,`.
|
||||
- Funções podem retornar múltiplos valores.
|
||||
- Existem dois valores de retorno com os nomes `output1` e `output2`, você pode omitir estes nomes e usar apenas os tipos deles.
|
||||
- Se existe apenas um valor de retorno e você omitir o nome, você não precisa usar colchetes nos valores de retorno.
|
||||
- Se a função não possui valores de retorno, você pode omitir os parâmetros de retorno completamente.
|
||||
- Se a função possui valores de retorno, você precisa utilizar a instrução `return` em algum lugar no corpo da função.
|
||||
|
||||
Let's see one practical example. (calculate maximum value)
|
||||
Vamos ver um exemplo prático. (calcular o valor máximo)
|
||||
|
||||
package main
|
||||
import "fmt"
|
||||
|
||||
// return greater value between a and b
|
||||
// retorna o maior valor entre a e b
|
||||
func max(a, b int) int {
|
||||
if a > b {
|
||||
return a
|
||||
@@ -215,26 +215,26 @@ Let's see one practical example. (calculate maximum value)
|
||||
y := 4
|
||||
z := 5
|
||||
|
||||
max_xy := max(x, y) // call function max(x, y)
|
||||
max_xz := max(x, z) // call function max(x, z)
|
||||
max_xy := max(x, y) // chama a função max(x, y)
|
||||
max_xz := max(x, z) // chama a função 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)) // call function here
|
||||
fmt.Printf("max(%d, %d) = %d\n", y, z, max(y,z)) // chama a função max aqui
|
||||
}
|
||||
|
||||
In the above example, there are two arguments in the function `max`, their types are both `int` so the first type can be omitted. For instance, `a, b int` instead of `a int, b int`. The same rules apply for additional arguments. Notice here that `max` only has one return value, so we only need to write the type of its return value -this is the short form of writing it.
|
||||
No exemplo acima existem dois argumentos do tipo `int` na função `max`, sendo assim o tipo do primeiro argumento pode ser omitido. Por exemplo, pode-se utilizar `a, b int` em vez de `a int, b int`. As mesmas regras se aplicam para argumentos adicionais. Observe aqui que `max` só tem um valor de retorno, então nós só precisamos escrever o tipo do valor de retorno. Esta é a forma curta de escrevê-lo.
|
||||
|
||||
### Multi-value return
|
||||
### Múltiplos valores de retorno
|
||||
|
||||
One thing that Go is better at than C is that it supports multi-value returns.
|
||||
Uma coisa em que Go é melhor que C é que ela suporta múltiplos valores de retorno.
|
||||
|
||||
We'll use the following example here.
|
||||
Usaremos o seguinte exemplo.
|
||||
|
||||
package main
|
||||
import "fmt"
|
||||
|
||||
// return results of A + B and A * B
|
||||
// Retorna os resultados de A + B e A * B
|
||||
func SumAndProduct(A, B int) (int, int) {
|
||||
return A+B, A*B
|
||||
}
|
||||
@@ -249,7 +249,7 @@ We'll use the following example here.
|
||||
fmt.Printf("%d * %d = %d\n", x, y, xTIMESy)
|
||||
}
|
||||
|
||||
The above example returns two values without names -you have the option of naming them also. If we named the return values, we would just need to use `return` to return the values since they are initialized in the function automatically. Notice that if your functions are going to be used outside of the package, which means your function names start with a capital letter, you'd better write complete statements for `return`; it makes your code more readable.
|
||||
O exemplo acima retorna dois valores sem nomes - você também tem a opção de nomeá-los se preferir. Se nomearmos os valores de retorno, poderemos utilizar apenas a instrução `return` para retornar os valores já que eles foram inicializados na função automaticamente. Observe que se suas funções forem utilizadas fora do pacote, o que significa que o nome das funções começam com uma letra maiúscula, é melhor escrever instruções completas para o `return`. Isto torna o seu código mais legível.
|
||||
|
||||
func SumAndProduct(A, B int) (add int, Multiplied int) {
|
||||
add = A+B
|
||||
@@ -257,85 +257,85 @@ The above example returns two values without names -you have the option of namin
|
||||
return
|
||||
}
|
||||
|
||||
### Variable arguments
|
||||
### Argumentos variáveis
|
||||
|
||||
Go supports variable arguments, which means you can give an uncertain numbers of argument to functions.
|
||||
Go suporta argumentos variáveis, o que significa que você pode dar um número incerto de argumentos para funções.
|
||||
|
||||
func myfunc(arg ...int) {}
|
||||
|
||||
`arg …int` tells Go that this is a function that has variable arguments. Notice that these arguments are type `int`. In the body of function, the `arg` becomes a `slice` of `int`.
|
||||
A instrução `arg …int` significa que esta é uma função que possui argumentos variáveis. Observe que estes argumentos são do tipo `int`. No corpo da função o `arg` se torna um `slice` de `int`.
|
||||
|
||||
for _, n := range arg {
|
||||
fmt.Printf("And the number is: %d\n", n)
|
||||
}
|
||||
|
||||
### Pass by value and pointers
|
||||
### Passar por valor e ponteiros
|
||||
|
||||
When we pass an argument to the function that was called, that function actually gets the copy of our variables so any change will not affect to the original variable.
|
||||
Quando passamos um argumento para uma função que foi chamada, a função recebe na verdade uma cópia da nossa variáveis original, sendo assim, as alterações não afetarão a variável original.
|
||||
|
||||
Let's see one example in order to prove what i'm saying.
|
||||
Vejamos um exemplo para provar o que eu estou dizendo.
|
||||
|
||||
package main
|
||||
import "fmt"
|
||||
|
||||
// simple function to add 1 to a
|
||||
// Função simples que adiciona 1 a variável a
|
||||
func add1(a int) int {
|
||||
a = a+1 // we change value of a
|
||||
return a // return new value of a
|
||||
a = a+1 // alteramos o valor de a
|
||||
return a // retornamos o novo valor de a
|
||||
}
|
||||
|
||||
func main() {
|
||||
x := 3
|
||||
|
||||
fmt.Println("x = ", x) // should print "x = 3"
|
||||
fmt.Println("x = ", x) // deve mostrar "x = 3"
|
||||
|
||||
x1 := add1(x) // call add1(x)
|
||||
x1 := add1(x) // chama add1(x)
|
||||
|
||||
fmt.Println("x+1 = ", x1) // should print "x+1 = 4"
|
||||
fmt.Println("x = ", x) // should print "x = 3"
|
||||
fmt.Println("x+1 = ", x1) // deve mostrar "x+1 = 4"
|
||||
fmt.Println("x = ", x) // deve mostrar "x = 3"
|
||||
}
|
||||
|
||||
Can you see that? Even though we called `add1` with `x`, the origin value of `x` doesn't change.
|
||||
Você consegue ver isso? Mesmo que nós chamamos `add1` com `x`, o valor original de `x` não é alterado.
|
||||
|
||||
The reason is very simple: when we called `add1`, we gave a copy of `x` to it, not the `x` itself.
|
||||
A razão é muito simples: quando chamamos `add1`, nós passamos uma cópia de `x` para a função, e não o próprio `x`.
|
||||
|
||||
Now you may ask how I can pass the real `x` to the function.
|
||||
Agora você pode se perguntar, como eu posso passar o `x` original para a função.
|
||||
|
||||
We need use pointers here. We know variables are stored in memory and they have some memory addresses. So, if we want to change the value of a variable, we must change its memory address. Therefore the function `add1` has to know the memory address of `x` in order to change its value. Here we pass `&x` to the function, and change the argument's type to the pointer type `*int`. Be aware that we pass a copy of the pointer, not copy of value.
|
||||
Precisamos usar ponteiros aqui. Sabemos que as variáveis são armazenadas em memória e que elas possuem endereços de memória. Então, se queremos alterar o valor de uma variável, precisamos alterar o valor armazenado no endereço de memória. Portanto, a função `add1` precisa saber o endereço de memória de `x` para poder alterar o seu valor. Para isto, passamos `&x` para a função, e alteramos o tipo do argumento para o tipo ponteiro `*int`. Esteja ciente de que nós passamos uma cópia do ponteiro, não uma cópia do valor.
|
||||
|
||||
package main
|
||||
import "fmt"
|
||||
|
||||
// simple function to add 1 to a
|
||||
// Função simples que adiciona 1 a variável a
|
||||
func add1(a *int) int {
|
||||
*a = *a+1 // we changed value of a
|
||||
return *a // return new value of a
|
||||
*a = *a+1 // alteramos o valor de a
|
||||
return *a // retornamos o novo valor de a
|
||||
}
|
||||
|
||||
func main() {
|
||||
x := 3
|
||||
|
||||
fmt.Println("x = ", x) // should print "x = 3"
|
||||
fmt.Println("x = ", x) // deve mostrar "x = 3"
|
||||
|
||||
x1 := add1(&x) // call add1(&x) pass memory address of x
|
||||
|
||||
fmt.Println("x+1 = ", x1) // should print "x+1 = 4"
|
||||
fmt.Println("x = ", x) // should print "x = 4"
|
||||
fmt.Println("x+1 = ", x1) // deve mostrar "x+1 = 4"
|
||||
fmt.Println("x = ", x) // deve mostrar "x = 4"
|
||||
}
|
||||
|
||||
Now we can change the value of `x` in the functions. Why do we use pointers? What are the advantages?
|
||||
Agora podemos alterar o valor de `x` dentro da função. Por que usamos ponteiros? Quais são as vantagens?
|
||||
|
||||
- Allows us to use more functions to operate on one variable.
|
||||
- Low cost by passing memory addresses (8 bytes), copy is not an efficient way, both in terms of time and space, to pass variables.
|
||||
- `string`, `slice`, `map` are reference types, so they use pointers when passing to functions by default. (Attention: If you need to change the length of `slice`, you have to pass pointers explicitly)
|
||||
- Permite-nos usar mais funções para operar em uma variável.
|
||||
- Baixo custo passando endereços de memória (8 bytes), a cópia não é uma maneira eficiente, tanto em termo de tempo como de espaço, para passar variáveis.
|
||||
- `string`, `slice`, `map` são tipos de referências, sendo assim, por padrão eles utilizam ponteiros ao passar para uma função. (Atenção: Se você precisa alterar o comprimento de um `slice`, você precisa passar ponteiros explicitamente)
|
||||
|
||||
### defer
|
||||
|
||||
Go has a well designed keyword called `defer`. You can have many `defer` statements in one function; they will execute in reverse order when the program executes to the end of functions. In the case where the program opens some resource files, these files would have to be closed before the function can return with errors. Let's see some examples.
|
||||
Go possui uma palavra-chave bem projetada chamada `defer`. Você pode ter muitas declarações `defer` em uma função. Elas serão executadas em ordem inversa quando o programa executa até o final das funções. No caso onde o programa abre alguns arquivos de recurso, estes arquivos precisam ser fechados antes que a função possa retornar com erros. Vamos ver alguns exemplos.
|
||||
|
||||
func ReadWrite() bool {
|
||||
file.Open("file")
|
||||
// Do some work
|
||||
// Faça alguma tarefa
|
||||
if failureX {
|
||||
file.Close()
|
||||
return false
|
||||
@@ -350,7 +350,7 @@ Go has a well designed keyword called `defer`. You can have many `defer` stateme
|
||||
return true
|
||||
}
|
||||
|
||||
We saw some code being repeated several times. `defer` solves this problem very well. It doesn't only help you to write clean code but also makes your code more readable.
|
||||
Vimos algum código sendo repetido várias vezes. `defer` resolve este problema muito bem. Ela não só ajuda você a escrever um código limpo, como também torna seu código mais legível.
|
||||
|
||||
func ReadWrite() bool {
|
||||
file.Open("file")
|
||||
@@ -364,24 +364,24 @@ We saw some code being repeated several times. `defer` solves this problem very
|
||||
return true
|
||||
}
|
||||
|
||||
If there are more than one `defer`s, they will execute by reverse order. The following example will print `4 3 2 1 0`.
|
||||
Se houver mais de uma instrução `defer`, elas serão executadas em ordem inversa. O exemplo a seguir irá mostrar `4 3 2 1 0`.
|
||||
|
||||
for i := 0; i < 5; i++ {
|
||||
defer fmt.Printf("%d ", i)
|
||||
}
|
||||
|
||||
### Functions as values and types
|
||||
### Funções como tipos e valores
|
||||
|
||||
Functions are also variables in Go, we can use `type` to define them. Functions that have the same signature can be seen as the same type.
|
||||
Funções também são variáveis em Go, podemos utilizar `type` para defini-las. Funções que possuem a mesma assinatura podem ser vistas como sendo do mesmo tipo.
|
||||
|
||||
type typeName func(input1 inputType1 , input2 inputType2 [, ...]) (result1 resultType1 [, ...])
|
||||
|
||||
What's the advantage of this feature? The answer is that it allows us to pass functions as values.
|
||||
Qual é a vantagem deste recurso? A resposta é que isto nos permite passar funções como valores.
|
||||
|
||||
package main
|
||||
import "fmt"
|
||||
|
||||
type testInt func(int) bool // define a function type of variable
|
||||
type testInt func(int) bool // define o tipo de uma função como variável
|
||||
|
||||
func isOdd(integer int) bool {
|
||||
if integer%2 == 0 {
|
||||
@@ -397,7 +397,7 @@ What's the advantage of this feature? The answer is that it allows us to pass fu
|
||||
return false
|
||||
}
|
||||
|
||||
// pass the function `f` as an argument to another function
|
||||
// passa a função `f` como um argumento para outra função
|
||||
|
||||
func filter(slice []int, f testInt) []int {
|
||||
var result []int
|
||||
@@ -412,23 +412,23 @@ What's the advantage of this feature? The answer is that it allows us to pass fu
|
||||
func main(){
|
||||
slice := []int {1, 2, 3, 4, 5, 7}
|
||||
fmt.Println("slice = ", slice)
|
||||
odd := filter(slice, isOdd) // use function as values
|
||||
odd := filter(slice, isOdd) // usa a função como valor
|
||||
fmt.Println("Odd elements of slice are: ", odd)
|
||||
even := filter(slice, isEven)
|
||||
fmt.Println("Even elements of slice are: ", even)
|
||||
}
|
||||
|
||||
It's very useful when we use interfaces. As you can see `testInt` is a variable that has function type, and return values and arguments of `filter` are the same as `testInt`. Therefore, we can have complex logic in our programs, while maintaining flexibility in our code.
|
||||
Isto é muito útil quando utilizamos interfaces. Como você pode ver `testInt` é uma variável que tem tipo de função, e os valores de retorno e argumentos de `filter` são os mesmos de `testInt`. Portanto, podemos ter lógica complexa em nossos programas mantendo a flexibilidade em nosso código.
|
||||
|
||||
### Panic and Recover
|
||||
### Panic e Recover
|
||||
|
||||
Go doesn't have `try-catch` structure like Java does. Instead of throwing exceptions, Go uses `panic` and `recover` to deal with errors. However, you shouldn't use `panic` very much, although it's powerful.
|
||||
Go não possui a estrutura `try-catch` assim como Java. Em vez de lançar exceções, Go usa `panic` e `recover` para lidar com erros. No entanto, embora poderoso, você não deve utilizar a instrução `panic` muito.
|
||||
|
||||
Panic is a built-in function to break the normal flow of programs and get into panic status. When a function `F` calls `panic`, `F` will not continue executing but its `defer` functions will continue to execute. Then `F` goes back to the break point which caused the panic status. The program will not terminate until all of these functions return with panic to the first level of that `goroutine`. `panic` can be produced by calling `panic` in the program, and some errors also cause `panic` like array access out of bounds errors.
|
||||
Panic é uma função interna para quebrar o fluxo normal de programas e entrar em estado de pânico. Quando uma função `F` chama `panic`, `F` não continuará executando, mas suas funções `defer` continuarão a ser executadas. Então `F` volta ao ponto de interrupção que causou o estado de pânico. O programa não terminará até que todas essas funções retornem com panic para o primeiro nível da `goroutine`. `panic` pode ser gerado executando a instrução `panic` no programa, e alguns erros também causam `panic`, como, por exemplo, a tentativa de acessar uma posição inválida em um array.
|
||||
|
||||
Recover is a built-in function to recover `goroutine`s from panic status. Calling `recover` in `defer` functions is useful because normal functions will not be executed when the program is in the panic status. It catches `panic` values if the program is in the panic status, and it gets `nil` if the program is not in panic status.
|
||||
Recover é uma função interna utilizada para recuperar goroutines de estados de pânico. Chamar `recover` nas funções `defer` é útil porque as funções normais não serão executadas quando o programa estiver em estado de pânico. Ele recebe os valores de `panic` se o programa está em estado de pânico, e recebe `nil` se o programa não está em estado de pânico.
|
||||
|
||||
The following example shows how to use `panic`.
|
||||
O seguinte exemplo mostra como utilizar `panic`.
|
||||
|
||||
var user = os.Getenv("USER")
|
||||
|
||||
@@ -438,7 +438,7 @@ The following example shows how to use `panic`.
|
||||
}
|
||||
}
|
||||
|
||||
The following example shows how to check `panic`.
|
||||
O seguinte exemplo mostra como verificar `panic`.
|
||||
|
||||
func throwsPanic(f func()) (b bool) {
|
||||
defer func() {
|
||||
@@ -446,71 +446,71 @@ The following example shows how to check `panic`.
|
||||
b = true
|
||||
}
|
||||
}()
|
||||
f() // if f causes panic, it will recover
|
||||
f() // se f causar pânico, ele irá recuperar
|
||||
return
|
||||
}
|
||||
|
||||
### `main` function and `init` function
|
||||
### Função `main` e função `init`
|
||||
|
||||
Go has two retentions which are called `main` and `init`, where `init` can be used in all packages and `main` can only be used in the `main` package. These two functions are not able to have arguments or return values. Even though we can write many `init` functions in one package, I strongly recommend writing only one `init` function for each package.
|
||||
Go possui duas retenções que são chamadas de `main` e `init`, onde `init` pode ser usada em todos os pacotes e `main` só pode ser usada no pacote `main`. Estas duas funções não são capazes de ter argumentos ou valores de retorno. Mesmo que possamos escrever muitas funções `init` em um pacote, recomendo fortemente escrever apenas uma função `init` para cada pacote.
|
||||
|
||||
Go programs will call `init()` and `main()` automatically, so you don't need to call them by yourself. For every package, the `init` function is optional, but `package main` has one and only one `main` function.
|
||||
Programas em Go irão chamar as funções `init()` e `main()` automaticamente, então você não precisa se preocupar em chamá-las. Para cada pacote, a função `init` é opcional, mas o `package main` tem uma e apenas uma função `main`.
|
||||
|
||||
Programs initialize and begin execution from the `main` package. If the `main` package imports other packages, they will be imported in the compile time. If one package is imported many times, it will be only compiled once. After importing packages, programs will initialize the constants and variables within the imported packages, then execute the `init` function if it exists, and so on. After all the other packages are initialized, programs will initialize constants and variables in the `main` package, then execute the `init` function inside the package if it exists. The following figure shows the process.
|
||||
Programas inicializam e começam a execução a partir do pacote `main`. Se o pacote `main` importa outros pacotes, eles serão importados em tempo de compilação. Se um pacote é importado muitas vezes, ele será compilado apenas uma vez. Depois de importar pacotes, os programas irão inicializar as constantes e variáveis dentro dos pacotes importados, e então executar a função `init` se ela existir, e assim por diante. Depois de todos os outros pacotes serem inicializados, os programas irão inicializar as constantes e variáveis do pacote `main` e então executar a função `init` dentro do pacote, se ela existir. A figura a seguir mostra o processo.
|
||||
|
||||

|
||||
|
||||
Figure 2.6 Flow of programs initialization in Go
|
||||
Figure 2.6 Fluxo de inicialização de programas em Go
|
||||
|
||||
### import
|
||||
|
||||
We use `import` very often in Go programs as follows.
|
||||
Usamos `import` muito frequentemente em programas Go da seguinte forma.
|
||||
|
||||
import(
|
||||
"fmt"
|
||||
)
|
||||
|
||||
Then we use functions in that package as follows.
|
||||
Então, usamos funções deste pacote da seguinte maneira.
|
||||
|
||||
fmt.Println("hello world")
|
||||
|
||||
`fmt` is from Go standard library, it is located within $GOROOT/pkg. Go supports third-party packages in two ways.
|
||||
`fmt` é da biblioteca padrão Go, que está localizada em $GOROOT/pkg. Go suporta pacotes de terceiros de duas maneiras.
|
||||
|
||||
1. Relative path
|
||||
import "./model" // load package in the same directory, I don't recommend this way.
|
||||
2. Absolute path
|
||||
import "shorturl/model" // load package in path "$GOPATH/pkg/shorturl/model"
|
||||
1. Caminho relativo
|
||||
import "./model" // carrega o pacote no mesmo diretório, eu não recomendo utilizar esta forma.
|
||||
2. Caminho absoluto
|
||||
import "shorturl/model" // carrega o pacote no caminho "$GOPATH/pkg/shorturl/model"
|
||||
|
||||
There are some special operators when we import packages, and beginners are always confused by these operators.
|
||||
Existem alguns operadores especiais quando importamos pacotes, e iniciantes na linguagem sempre se confundem com estes operadores.
|
||||
|
||||
1. Dot operator.
|
||||
Sometime we see people use following way to import packages.
|
||||
1. Operador: ponto.
|
||||
Às vezes vemos pessoas usando a seguinte forma para importar pacotes.
|
||||
|
||||
import(
|
||||
. "fmt"
|
||||
)
|
||||
|
||||
The dot operator means you can omit the package name when you call functions inside of that package. Now `fmt.Printf("Hello world")` becomes to `Printf("Hello world")`.
|
||||
2. Alias operation.
|
||||
It changes the name of the package that we imported when we call functions that belong to that package.
|
||||
O operador ponto significa que você pode omitir o nome do pacote quando você chamar funções dentro deste pacote. Assim sendo, `fmt.Printf("Hello world")` torna-se `Printf("Hello world")`.
|
||||
2. Operador: pseudônimo (alias)
|
||||
Ele altera o nome do pacote que importamos quando chamamos funções que pertencem a este pacote.
|
||||
|
||||
import(
|
||||
f "fmt"
|
||||
)
|
||||
|
||||
Now `fmt.Printf("Hello world")` becomes to `f.Printf("Hello world")`.
|
||||
3. `_` operator.
|
||||
This is the operator that is difficult to understand without someone explaining it to you.
|
||||
Assim sendo, `fmt.Printf("Hello world")` torna-se `f.Printf("Hello world")`.
|
||||
3. Operador: `_`.
|
||||
Este operador é difícil de entender sem alguém explicando para você.
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
_ "github.com/ziutek/mymysql/godrv"
|
||||
)
|
||||
|
||||
The `_` operator actually means we just want to import that package and execute its `init` function, and we are not sure if want to use the functions belonging to that package.
|
||||
O operador `_` na verdade significa que só queremos importar este pacote e executar sua função `init`, e não temos certeza se queremos usar as funções pertencentes a este pacote.
|
||||
|
||||
## Links
|
||||
|
||||
- [Directory](preface.md)
|
||||
- Previous section: [Go foundation](02.2.md)
|
||||
- Next section: [struct](02.4.md)
|
||||
- [Sumário](preface.md)
|
||||
- Seção anterior: [Fundamentos em Go](02.2.md)
|
||||
- Próxima seção: [struct](02.4.md)
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
// Example code for Chapter 2.3 from "Build Web Application with Golang"
|
||||
// Purpose: Creating a basic function
|
||||
// Código de exemplo do capítulo 2.3 de "Build Web Application with Golang"
|
||||
// Propósito: Criando uma função básica
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
// return greater value between a and b
|
||||
// retorna o maior valor entre a e b
|
||||
func max(a, b int) int {
|
||||
if a > b {
|
||||
return a
|
||||
@@ -17,10 +17,10 @@ func main() {
|
||||
y := 4
|
||||
z := 5
|
||||
|
||||
max_xy := max(x, y) // call function max(x, y)
|
||||
max_xz := max(x, z) // call function max(x, z)
|
||||
max_xy := max(x, y) // chama a função max(x, y)
|
||||
max_xz := max(x, z) // chama a função 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)) // call function here
|
||||
fmt.Printf("max(%d, %d) = %d\n", y, z, max(y, z)) // chama a função aqui
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// As of Google go 1.1.2, `println()` and `print()` are hidden functions included from the runtime package.
|
||||
// However it's encouraged to use the print functions from the `fmt` package.
|
||||
// A partir do Google go 1.1.2, `println()` e `print()` são funções ocultas incluídas no pacote de tempo de execução.
|
||||
// No entanto, é encorajado utilizar as funções de impressão do pacote `fmt`
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
@@ -1,25 +1,24 @@
|
||||
// Example code for Chapter 2.3 from "Build Web Application with Golang"
|
||||
// Purpose: Shows different ways of importing a package.
|
||||
// Note: For the package `only_call_init`, we reference the path from the
|
||||
// base directory of `$GOPATH/src`. The reason being Golang discourage
|
||||
// the use of relative paths when import packages.
|
||||
// Código de exemplo do capítulo 2.3 de "Build Web Application with Golang"
|
||||
// Propósito: mostra diferentes formas de importar um pacote.
|
||||
// Nota: para o pacote `only_call_init`, fazemos referência ao caminho a partir do diretório
|
||||
// base de `$GOPATH/src`. Golang desencoraja o uso de caminhos relativos para importar pacotes.
|
||||
// BAD: "./only_call_init"
|
||||
// GOOD: "apps/ch.2.3/import_packages/only_call_init"
|
||||
package main
|
||||
|
||||
import (
|
||||
// `_` will only call init() inside the package only_call_init
|
||||
// `_` irá chamar apenas init() dentro do pacote only_call_init
|
||||
_ "apps/ch.2.3/import_packages/only_call_init"
|
||||
f "fmt" // import the package as `f`
|
||||
. "math" // makes the public methods and constants global
|
||||
"mymath" // custom package located at $GOPATH/src/
|
||||
"os" // normal import of a standard package
|
||||
"text/template" // the package takes the name of last folder path, `template`
|
||||
f "fmt" // importa o pacote como `f`
|
||||
. "math" // torna os métodos públicos e constantes globais
|
||||
"mymath" // pacote personalizado localizado em $GOPATH/src/
|
||||
"os" // import normal de um pacote padrão
|
||||
"text/template" // o pacote leva o nome do último caminho da pasta, `template`
|
||||
)
|
||||
|
||||
func main() {
|
||||
f.Println("mymath.Sqrt(4) =", mymath.Sqrt(4))
|
||||
f.Println("E =", E) // references math.E
|
||||
f.Println("E =", E) // referencia math.E
|
||||
|
||||
t, _ := template.New("test").Parse("Pi^2 = {{.}}")
|
||||
t.Execute(os.Stdout, Pow(Pi, 2))
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Example code for Chapter 2.3 from "Build Web Application with Golang"
|
||||
// Purpose: Goes over if, else, switch conditions, loops and defer.
|
||||
// Código de exemplo do capítulo 2.3 de "Build Web Application with Golang"
|
||||
// Propósito: mostra alguns exemplos de if, else, switch, loops e defer.
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
@@ -30,25 +30,25 @@ func show_if() {
|
||||
}
|
||||
func show_if_var() {
|
||||
fmt.Println("\n#show_if_var()")
|
||||
// initialize x, then check if x greater than
|
||||
// inicializa x, então verifica se x é maior
|
||||
if x := computedValue(); x > 10 {
|
||||
fmt.Println("x is greater than 10")
|
||||
} else {
|
||||
fmt.Println("x is less than 10")
|
||||
}
|
||||
|
||||
// the following code will not compile, since `x` is only accessible with the if/else block
|
||||
// o seguinte código não irá compilar, porque `x` é acessível apenas pelo bloco if/else
|
||||
// fmt.Println(x)
|
||||
}
|
||||
func show_goto() {
|
||||
fmt.Println("\n#show_goto()")
|
||||
// The call to the label switches the goroutine it seems.
|
||||
// A chamada para o label altera o fluxo da goroutine.
|
||||
i := 0
|
||||
Here: // label ends with ":"
|
||||
Here: // label termina com ":"
|
||||
fmt.Println(i)
|
||||
i++
|
||||
if i < 10 {
|
||||
goto Here // jump to label "Here"
|
||||
goto Here // pule para label "Here"
|
||||
}
|
||||
}
|
||||
func show_for_loop() {
|
||||
@@ -60,7 +60,7 @@ func show_for_loop() {
|
||||
fmt.Println("part 1, sum is equal to ", sum)
|
||||
|
||||
sum = 1
|
||||
// The compiler will remove the `;` from the line below.
|
||||
// O compilador irá remover o `;` da linha abaixo.
|
||||
// for ; sum < 1000 ; {
|
||||
for sum < 1000 {
|
||||
sum += sum
|
||||
@@ -69,7 +69,7 @@ func show_for_loop() {
|
||||
|
||||
for index := 10; 0 < index; index-- {
|
||||
if index == 5 {
|
||||
break // or continue
|
||||
break // ou continue
|
||||
}
|
||||
fmt.Println(index)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Example code for Chapter 2.3 from "Build Web Application with Golang"
|
||||
// Purpose: Showing how to use `panic()` and `recover()`
|
||||
// Código de exemplo do capítulo 2.3 de "Build Web Application with Golang"
|
||||
// Propósito: mostrar como usar `panic()` e `recover()`
|
||||
package main
|
||||
|
||||
import (
|
||||
@@ -22,7 +22,7 @@ func throwsPanic(f func()) (b bool) {
|
||||
b = true
|
||||
}
|
||||
}()
|
||||
f() // if f causes panic, it will recover
|
||||
f() // se f causar pânico, isto irá recuperar
|
||||
return
|
||||
}
|
||||
func main(){
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Example code for Chapter 2.3 from "Build Web Application with Golang"
|
||||
// Purpose: Shows passing a variable by value and reference
|
||||
// Código de exemplo do capítulo 2.3 de "Build Web Application with Golang"
|
||||
// Propósito: mostra como passar uma variável por valor e por referência
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
@@ -21,7 +21,7 @@ func show_add_by_value() {
|
||||
func show_add_by_reference() {
|
||||
x := 3
|
||||
fmt.Println("x = ", x)
|
||||
// &x pass memory address of x
|
||||
// &x passa o endereço de memória de x
|
||||
fmt.Println("add_by_reference(&x) =", add_by_reference(&x) )
|
||||
fmt.Println("x = ", x)
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
// Example code for Chapter 2.3 from "Build Web Application with Golang"
|
||||
// Purpose: Shows how to define a function type
|
||||
// Código de exemplo do capítulo 2.3 de "Build Web Application with Golang"
|
||||
// Propósito: mostra como definir um tipo de função
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type testInt func(int) bool // define a function type of variable
|
||||
type testInt func(int) bool // define uma função como um tipo de variável
|
||||
|
||||
func isOdd(integer int) bool {
|
||||
if integer%2 == 0 {
|
||||
@@ -20,7 +20,7 @@ func isEven(integer int) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// pass the function `f` as an argument to another function
|
||||
// passa a função `f` como um argumento para outra função
|
||||
|
||||
func filter(slice []int, f testInt) []int {
|
||||
var result []int
|
||||
@@ -37,7 +37,7 @@ func init() {
|
||||
func main() {
|
||||
slice := []int{1, 2, 3, 4, 5, 7}
|
||||
fmt.Println("slice = ", slice)
|
||||
odd := filter(slice, isOdd) // use function as values
|
||||
odd := filter(slice, isOdd) // usa funções como valores
|
||||
fmt.Println("Odd elements of slice are: ", odd)
|
||||
even := filter(slice, isEven)
|
||||
fmt.Println("Even elements of slice are: ", even)
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
// Example code for Chapter 2.3 from "Build Web Application with Golang"
|
||||
// Purpose: Shows how to return multiple values from a function
|
||||
// Código de exemplo do capítulo 2.3 de "Build Web Application with Golang"
|
||||
// Propósito: mostra como retornar múltiplos valores de uma função
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
// return results of A + B and A * B
|
||||
// retorna os resultados de A + B e A * B
|
||||
func SumAndProduct(A, B int) (int, int) {
|
||||
return A + B, A * B
|
||||
}
|
||||
|
||||
211
ru/05.1.md
Normal file
211
ru/05.1.md
Normal file
@@ -0,0 +1,211 @@
|
||||
# 5.1 Интерфейс database/sql
|
||||
|
||||
Как я уже говорил, Go не предоставляет официальных драйверов баз данных, как это делает, например, PHP, но у него есть некоторые стандарты интерфейсов драйверов для разработчиков драйверов баз данных. Если ваш код будет разрабатываться в соответствии с этими стандартами, вам не придется его менять в случае изменения базы данных. Давайте посмотрим, что представляют из себя эти стандарты интерфейсов.
|
||||
|
||||
## sql.Register
|
||||
|
||||
Эта функция из пакета `database/sql` обеспечивает регистрацию драйверов баз данных сторонних разработчиков. Все сторонние драйвера должны вызывать функцию `Register(name string, driver driver.Driver)` в функции `init()` для того чтобы обеспечить регистрацию драйвера.
|
||||
|
||||
Давайте посмотрим на соответствующий код в драйверах mymysql и sqlite3:
|
||||
|
||||
//https://github.com/mattn/go-sqlite3 driver
|
||||
func init() {
|
||||
sql.Register("sqlite3", &SQLiteDriver{})
|
||||
}
|
||||
|
||||
//https://github.com/mikespook/mymysql driver
|
||||
// Driver automatically registered in database/sql
|
||||
var d = Driver{proto: "tcp", raddr: "127.0.0.1:3306"}
|
||||
func init() {
|
||||
Register("SET NAMES utf8")
|
||||
sql.Register("mymysql", &d)
|
||||
}
|
||||
|
||||
Мы видим, что все сторонние драйвера вызывают данную функцию для саморегистрации. При этом Go использует карту для сохранения информации о пользовательском драйвере внутри `databse/sql`.
|
||||
|
||||
var drivers = make(map[string]driver.Driver)
|
||||
|
||||
drivers[name] = driver
|
||||
|
||||
Таким образом, данная функция может зарегистрировать любое количество драйверов с разными именами.
|
||||
|
||||
Если посмотреть примеры использования сторонних драйверов, мы всегда увидим следующий код:
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
Бланк `_`, в примере, приводит в замешательство многих начинающих программистов, но он является примером отличного дизайна языка Go. Как вы знаете, этот идентификатор предназначен для удаления значений из функции return. Так же вы должны знать, что вы должны использовать все импортированные пакеты в коде. В данном случае, использование бланка при импорте означает, что вам необходимо исполнить функцию init() данного пакета без его прямого использования, которая, в свою очередь, используется для регистрации драйвера базы данных.
|
||||
|
||||
## driver.Driver
|
||||
|
||||
`Driver` - это интерфейс, который имеет метод `Open(name string)` который возвращает интерфейс `Conn`.
|
||||
|
||||
type Driver interface {
|
||||
Open(name string) (Conn, error)
|
||||
}
|
||||
|
||||
`Conn` - это одноразовое соединение. Это означает, что его можно использовать только один раз в одной горутине. Следующй код вызовет исключение:
|
||||
|
||||
...
|
||||
go goroutineA (Conn) // запрос
|
||||
go goroutineB (Conn) // вставка
|
||||
...
|
||||
|
||||
потому, что Go не имеет представления в какой горутине выполняется операция. Операция "запрос" может получить результаты "вставки" и наоборот.
|
||||
|
||||
Все сторонние драйверы должны реализовывать эту функцию для разбора имени соединения и корректного возврата результатов.
|
||||
|
||||
## driver.Conn
|
||||
|
||||
Driver.Conn - это интерфейс подключения базы данных с несколькими методами.
|
||||
|
||||
type Conn interface {
|
||||
Prepare(query string) (Stmt, error)
|
||||
Close() error
|
||||
Begin() (Tx, error)
|
||||
}
|
||||
|
||||
- `Prepare` - возвращает статус подготовки соответствующих команд SQL для запроса, удаления и т.д.
|
||||
- `Close` - закрывает текущее соединение и освобождает ресурсы. Большинство сторонних драйверов реализуют пулы соединений самостоятельно, поэтому вам не стоит подключать кэш соединений если вы не хотите иметь неожиданных ошибок.
|
||||
- `Begin` - запускает и возвращает дескриптор новой транзакции `Tx`, вы можете использовать его для запросов, апдейтов, отката транзакций и т.д.
|
||||
|
||||
## driver.Stmt
|
||||
|
||||
`Stmt` - это интерфейс состояния готовности, соответствующий данному соединению, поэтому он может быть использован только в одной горутине, как и `Conn`.
|
||||
|
||||
type Stmt interface {
|
||||
Close() error
|
||||
NumInput() int
|
||||
Exec(args []Value) (Result, error)
|
||||
Query(args []Value) (Rows, error)
|
||||
}
|
||||
|
||||
- `Close` - закрывает текущее соединение, но по прежнему возвращает строки данных, если запрос продолжает выполнятся.
|
||||
- `NumInput` - возвращает число обязательных аргументов, драйвер базы данных должен проверить аргументы вызывающей стороны и вернуть результат больше 0 и -1 в случае, если драйвер не смог определить количество аргументов.
|
||||
- `Exec` выполняет SQL - команды `update/insert`, которые были подготовлены в `Prepare`. Возвращает `Result`.
|
||||
- `Query` - выполняет SQL - команды `select`, которые были подготовлены в `Prepare`. Возвращает `Result`.
|
||||
|
||||
## driver.Tx
|
||||
|
||||
Данный интерфейс, как правило, управляет операциями commit и roll back. Драйвер базы данных должен реализовать эти два метода.
|
||||
|
||||
type Tx interface {
|
||||
Commit() error
|
||||
Rollback() error
|
||||
}
|
||||
|
||||
## driver.Execer
|
||||
|
||||
Это необязательный интерфейс.
|
||||
|
||||
type Execer interface {
|
||||
Exec(query string, args []Value) (Result, error)
|
||||
}
|
||||
|
||||
Если драйвер не реализует этот интерфейс, то при вызове `DB.Exec`, он автоматически вызывает `Prepare` и возвращает `Stmt`, выполняет `Exec` из `Stmt` затем закрывает `Stmt`.
|
||||
|
||||
## driver.Result
|
||||
|
||||
Этот интерфейс возвращает результаты операций `update/insert`.
|
||||
|
||||
type Result interface {
|
||||
LastInsertId() (int64, error)
|
||||
RowsAffected() (int64, error)
|
||||
}
|
||||
|
||||
- `LastInsertId` - возвращает автоинкрементный Id после операций вставки в базу данных.
|
||||
- `RowsAffected` - возвращает номера строк, затронутых операцией `update/insert`.
|
||||
|
||||
## driver.Rows
|
||||
|
||||
driver.Rows - это интерфейс для возвращения результатов набора операций запроса.
|
||||
|
||||
type Rows interface {
|
||||
Columns() []string
|
||||
Close() error
|
||||
Next(dest []Value) error
|
||||
}
|
||||
|
||||
- `Columns` - возвращает информацию о наименовании столбцов таблицы.
|
||||
- `Close` - закрывает итератор строк.
|
||||
- `Next` - возвращает следующее поле таблицы и связывает с `dest`, все строки должны быть преобразованы к массиву байт. Если доступные данные закончились, возникает ошибка `io.EOF`.
|
||||
|
||||
## diriver.RowsAffected
|
||||
|
||||
Данный тип является псевдонимом int64 для одноименного метода реализованного в интерфейсе `Result`.
|
||||
|
||||
type RowsAffected int64
|
||||
|
||||
func (RowsAffected) LastInsertId() (int64, error)
|
||||
|
||||
func (v RowsAffected) RowsAffected() (int64, error)
|
||||
|
||||
## driver.Value
|
||||
|
||||
Это пустой интерфейс, который может содержать любой тип данных.
|
||||
|
||||
type Value interface{}
|
||||
|
||||
`Value` должно быть содержать значения, совместимые с драйвером, или быть nil, поэтому оно должно быть одним из следующих типов:
|
||||
|
||||
int64
|
||||
float64
|
||||
bool
|
||||
[]byte
|
||||
string [*] За исключением `Rows.Next`, который не может возвращать строку.
|
||||
time.Time
|
||||
|
||||
## driver.ValueConverter
|
||||
|
||||
`ValueConverter` - это интерфейс для преобразования наших данных к типам `driver.Value`.
|
||||
|
||||
type ValueConverter interface {
|
||||
ConvertValue(v interface{}) (Value, error)
|
||||
}
|
||||
|
||||
Обычно он используется в драйверах баз данных и предоставляет массу полезных особенностей:
|
||||
|
||||
- Преобразует `driver.Value` в соответствующее типу поля базы данных. Например преобразует int64 в uint16.
|
||||
- Преобразует результаты запросов к `driver.Value`.
|
||||
- Преобразует `driver.Value` к определенному пользователем значению в функции `scan`.
|
||||
|
||||
## driver.Valuer
|
||||
|
||||
`Valuer` - определяет интерфейс для возврата ` driver.Value`.
|
||||
|
||||
type Valuer interface {
|
||||
Value() (Value, error)
|
||||
}
|
||||
|
||||
Многие типы реализуют этот интерфейс для преобразования типов между собой и driver.Value.
|
||||
|
||||
На данном этапе вы должны иметь представление о том, как разработать драйвер базы данных. После того, как вы реализуете интерфейсы для различных операций, таких как добавление, удаление, обновление и так далее, останется решить проблему коммуникации с конкретной базой данных.
|
||||
|
||||
## database/sql
|
||||
|
||||
`databse/sql` определяет высокоуровневые методы для более удобной работы с базами данных (выше чем драйвера) и предлагает вам реализовать пул соединений.
|
||||
|
||||
type DB struct {
|
||||
driver driver.Driver
|
||||
dsn string
|
||||
mu sync.Mutex // защищает и закрывает freeConn
|
||||
freeConn []driver.Conn
|
||||
closed bool
|
||||
}
|
||||
|
||||
Как вы видите, функция `Open` возвращает тип `DB`, который содержит freeConn, - это и есть простой пулл. Его реализация очень проста и несколько уродлива. Он использует `defer db.putConn(ci, err)` в функции `Db.prepare` для помещения соединения в пулл соединений. Каждый раз, когда вызывается функция `Conn`, он проверяет длину ` freeConn` и, если она больше нуля, - это означает, что соединение можно повторно использовать и оно напрямую возвращается вам. В противном случае он создает новое соединение и возвращает его.
|
||||
|
||||
## Ссылки
|
||||
|
||||
- [Содержание](preface.md)
|
||||
- Предыдущий раздел: [Базы данных](05.0.md)
|
||||
- Следующий раздел: [MySQL](05.2.md)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
129
ru/05.2.md
Normal file
129
ru/05.2.md
Normal file
@@ -0,0 +1,129 @@
|
||||
# 5.2 MySQL
|
||||
|
||||
Комплекс северного программного обеспечения LAMP последние годы очень популярен в интернете. Буква М в данном акрониме означает MySQL. MySQL является наиболее известной базой данных с открытым исходным кодом. Она проста в использовании, поэтому ее часто используют в бэкенде множества веб-сайтов.
|
||||
|
||||
## Драйвера MySQL
|
||||
|
||||
Есть несколько драйверов, которые поддерживают MySQL в Go, некоторые из них включают `database/sql`, некоторые используют только стандарты интерфейсов.
|
||||
|
||||
- [https://github.com/go-sql-driver/mysql](https://github.com/go-sql-driver/mysql) поддерживает `database/sql`, чистый код Go.
|
||||
- [https://github.com/ziutek/mymysql](https://github.com/ziutek/mymysql) поддерживает `database/sql` и определенные пользователем интерфейсы, чистый код Go.
|
||||
- [https://github.com/Philio/GoMySQL](https://github.com/Philio/GoMySQL) поддерживает только определенные пользователем интерфейсы, чистый код Go.
|
||||
|
||||
Я буду использовать первый драйвер в моих будущих примерах (в моих проекта я тоже использую его). Я рекомендую вам использовать его по следующим причинам:
|
||||
|
||||
- Это новый драйвер базы данных и он предоставляет множество возможностей.
|
||||
- Полностью поддерживает стандарты интерфейса `databse/sql`.
|
||||
- Поддерживает постоянное соединение между потоками.
|
||||
|
||||
##Примеры
|
||||
|
||||
Во всех следующих разделах я буду использовать одинаковую структуру таблицы базы данных для различных баз данных, создать которую можно следующим SQL запросом:
|
||||
|
||||
CREATE TABLE `userinfo` (
|
||||
`uid` INT(10) NOT NULL AUTO_INCREMENT,
|
||||
`username` VARCHAR(64) NULL DEFAULT NULL,
|
||||
`departname` VARCHAR(64) NULL DEFAULT NULL,
|
||||
`created` DATE NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`uid`)
|
||||
);
|
||||
|
||||
В следующем примере показано, как работать с базой данных на основе стандартов интерфейса `database/sql`.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
)
|
||||
|
||||
func main() {
|
||||
db, err := sql.Open("mysql", "astaxie:astaxie@/test?charset=utf8")
|
||||
checkErr(err)
|
||||
|
||||
// вставка
|
||||
stmt, err := db.Prepare("INSERT userinfo SET username=?,departname=?,created=?")
|
||||
checkErr(err)
|
||||
|
||||
res, err := stmt.Exec("astaxie", "研发部门", "2012-12-09")
|
||||
checkErr(err)
|
||||
|
||||
id, err := res.LastInsertId()
|
||||
checkErr(err)
|
||||
|
||||
fmt.Println(id)
|
||||
// обновление
|
||||
stmt, err = db.Prepare("update userinfo set username=? where uid=?")
|
||||
checkErr(err)
|
||||
|
||||
res, err = stmt.Exec("astaxieupdate", id)
|
||||
checkErr(err)
|
||||
|
||||
affect, err := res.RowsAffected()
|
||||
checkErr(err)
|
||||
|
||||
fmt.Println(affect)
|
||||
|
||||
// запрос
|
||||
rows, err := db.Query("SELECT * FROM userinfo")
|
||||
checkErr(err)
|
||||
|
||||
for rows.Next() {
|
||||
var uid int
|
||||
var username string
|
||||
var department string
|
||||
var created string
|
||||
err = rows.Scan(&uid, &username, &department, &created)
|
||||
checkErr(err)
|
||||
fmt.Println(uid)
|
||||
fmt.Println(username)
|
||||
fmt.Println(department)
|
||||
fmt.Println(created)
|
||||
}
|
||||
|
||||
// удаление
|
||||
stmt, err = db.Prepare("delete from userinfo where uid=?")
|
||||
checkErr(err)
|
||||
|
||||
res, err = stmt.Exec(id)
|
||||
checkErr(err)
|
||||
|
||||
affect, err = res.RowsAffected()
|
||||
checkErr(err)
|
||||
|
||||
fmt.Println(affect)
|
||||
|
||||
db.Close()
|
||||
|
||||
}
|
||||
|
||||
func checkErr(err error) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
Позвольте мне объяснить несколько важных функций:
|
||||
|
||||
- `sql.Open()` - открывает зарегистрированный драйвер базы данных. В нашем примере это Go-MySQL-Driver. Второй аргумент в данной функции это DSN (Data Source Name), который определяет набор информации, необходимой для подключения к базе данных. Он поддерживает следующие форматы:
|
||||
|
||||
User@UNIX(/path/to/socket)/dbname? charset = utf8
|
||||
user:password@tcp(localhost:5555)/dbname?charset=utf8
|
||||
user:password@/dbname
|
||||
user:password@tcp([de:ad:be:ef::ca:fe]:80)/dbname
|
||||
|
||||
- `db.Prepare()` - возвращает SQL операцию, которая будет выполнятся, так же возвращает статус исполнения после исполнения SQL запроса.
|
||||
- `db.Query()` - исполняет SQL и возвращает результат в виде набора строк.
|
||||
- `stmt.Exec()` - выполняет SQL, который подготовлен в Stmt.
|
||||
|
||||
Обратите внимание, что мы используем формат «=?» для передачи аргументов. Это делается для предотвращения SQL-инъекций.
|
||||
|
||||
## Ссылки
|
||||
|
||||
- [Содержание](preface.md)
|
||||
- Предыдущий раздел: [Интерфейс database/sql](05.1.md)
|
||||
- Следующий раздел: [SQLite](05.3.md)
|
||||
|
||||
|
||||
113
ru/05.3.md
Normal file
113
ru/05.3.md
Normal file
@@ -0,0 +1,113 @@
|
||||
# 5.3 SQLite
|
||||
|
||||
SQLite это открытая встраиваемая реляционная база данных. Она автономна, не требует конфигурации и является полноценной СУБД. Ее основные характеристики это: высокая портативность, простота использования, эффективность и надежость. В большинстве случаев вам нужен только двоичный файл SQLite для создания, подключения и эксплуатации базы данных. Если вы ищите встраиваемое решение СУБД, вам стоит рассмотреть SQLite. По сути SQLite является версией Access с открыты исходным кодом.
|
||||
|
||||
## Драйвера SQLite
|
||||
|
||||
В Go есть множество драйверов баз данных для SQLite, но многие из них не поддерживают стандарты интерфейсов `database/sql`.
|
||||
|
||||
- [https://github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3) поддерживает `database/sql`, базируется на cgo.
|
||||
- [https://github.com/feyeleanor/gosqlite3](https://github.com/feyeleanor/gosqlite3) не поддерживает `database/sql`, базируется на cgo.
|
||||
- [https://github.com/phf/go-sqlite3](https://github.com/phf/go-sqlite3) не поддерживает `database/sql`, базируется на cgo.
|
||||
|
||||
Первый драйвер является единственным, который поддерживает стандарты интерфейса `database/sql` в SQLite, поэтому я использую его в моих проектах. Поддержка стандартов позволит легко мигрировать на другую базу в будущем.
|
||||
|
||||
##Примеры
|
||||
|
||||
Создайте таблицу следующим запросом:
|
||||
|
||||
CREATE TABLE `userinfo` (
|
||||
`uid` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
`username` VARCHAR(64) NULL,
|
||||
`departname` VARCHAR(64) NULL,
|
||||
`created` DATE NULL
|
||||
);
|
||||
|
||||
Пример:
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
func main() {
|
||||
db, err := sql.Open("sqlite3", "./foo.db")
|
||||
checkErr(err)
|
||||
|
||||
// вставка
|
||||
stmt, err := db.Prepare("INSERT INTO userinfo(username, departname, created) values(?,?,?)")
|
||||
checkErr(err)
|
||||
|
||||
res, err := stmt.Exec("astaxie", "研发部门", "2012-12-09")
|
||||
checkErr(err)
|
||||
|
||||
id, err := res.LastInsertId()
|
||||
checkErr(err)
|
||||
|
||||
fmt.Println(id)
|
||||
// обновление
|
||||
stmt, err = db.Prepare("update userinfo set username=? where uid=?")
|
||||
checkErr(err)
|
||||
|
||||
res, err = stmt.Exec("astaxieupdate", id)
|
||||
checkErr(err)
|
||||
|
||||
affect, err := res.RowsAffected()
|
||||
checkErr(err)
|
||||
|
||||
fmt.Println(affect)
|
||||
|
||||
// запрос
|
||||
rows, err := db.Query("SELECT * FROM userinfo")
|
||||
checkErr(err)
|
||||
|
||||
for rows.Next() {
|
||||
var uid int
|
||||
var username string
|
||||
var department string
|
||||
var created string
|
||||
err = rows.Scan(&uid, &username, &department, &created)
|
||||
checkErr(err)
|
||||
fmt.Println(uid)
|
||||
fmt.Println(username)
|
||||
fmt.Println(department)
|
||||
fmt.Println(created)
|
||||
}
|
||||
|
||||
// удаление
|
||||
stmt, err = db.Prepare("delete from userinfo where uid=?")
|
||||
checkErr(err)
|
||||
|
||||
res, err = stmt.Exec(id)
|
||||
checkErr(err)
|
||||
|
||||
affect, err = res.RowsAffected()
|
||||
checkErr(err)
|
||||
|
||||
fmt.Println(affect)
|
||||
|
||||
db.Close()
|
||||
|
||||
}
|
||||
|
||||
func checkErr(err error) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Вы наверняка заметили, что код очень похож на пример из предыдущего раздела, и мы изменили только имя зарегистрированного драйвера и вызвали `sql.Open` для соединения с SQLite по-другому.
|
||||
|
||||
Ниже дана ссылка на инструмент управления SQLite: [http://sqliteadmin.orbmu2k.de/](http://sqliteadmin.orbmu2k.de/)
|
||||
|
||||
## Ссылки
|
||||
|
||||
- [Содержание](preface.md)
|
||||
- Предыдущий раздел: [MySQL](05.2.md)
|
||||
- Следующий раздел: [PostgreSQL](05.4.md)
|
||||
|
||||
127
ru/05.4.md
Normal file
127
ru/05.4.md
Normal file
@@ -0,0 +1,127 @@
|
||||
# 5.4 PostgreSQL
|
||||
|
||||
PostgreSQL - свободная объектно-реляционная система управления базами данных доступная для множества платформ, включая Linux, FreeBSD, Solaris, Microsoft Windows и Mac OS X. Выпускается под собственной MIT-подобной лицензией. В отличие от MySQL, она разработана и позиционируется для использования в корпоративном сегменте, как Oracle. Так что PostgreSQL - это хороший выбор для использования в корпоративных проектах.
|
||||
|
||||
## Драйвера PostgreSQL
|
||||
|
||||
Есть множество драйверов баз данных для PostgreSQL, мы рассмотрим три из них:
|
||||
|
||||
- [https://github.com/bmizerany/pq](https://github.com/bmizerany/pq) поддерживает `database/sql`, чистый код Go.
|
||||
- [https://github.com/jbarham/gopgsqldriver](https://github.com/jbarham/gopgsqldriver) поддерживает `database/sql`, чистый код Go.
|
||||
- [https://github.com/lxn/go-pgsql](https://github.com/lxn/go-pgsql) поддерживает `database/sql`, чистый код Go.
|
||||
|
||||
Я буду использовать первый драйвер в последующих примерах.
|
||||
|
||||
##Примеры
|
||||
|
||||
Создайте таблицу следующим запросом:
|
||||
|
||||
CREATE TABLE userinfo
|
||||
(
|
||||
uid serial NOT NULL,
|
||||
username character varying(100) NOT NULL,
|
||||
departname character varying(500) NOT NULL,
|
||||
Created date,
|
||||
CONSTRAINT userinfo_pkey PRIMARY KEY (uid)
|
||||
)
|
||||
WITH (OIDS=FALSE);
|
||||
|
||||
Пример:
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
_ "github.com/bmizerany/pq"
|
||||
)
|
||||
|
||||
const (
|
||||
DB_USER = "postgres"
|
||||
DB_PASSWORD = "postgres"
|
||||
DB_NAME = "test"
|
||||
)
|
||||
|
||||
func main() {
|
||||
dbinfo := fmt.Sprintf("user=%s password=%s dbname=%s sslmode=disable",
|
||||
DB_USER, DB_PASSWORD, DB_NAME)
|
||||
db, err := sql.Open("postgres", dbinfo)
|
||||
checkErr(err)
|
||||
defer db.Close()
|
||||
|
||||
fmt.Println("# Inserting values")
|
||||
|
||||
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("last inserted id =", lastInsertId)
|
||||
|
||||
fmt.Println("# Updating")
|
||||
stmt, err := db.Prepare("update userinfo set username=$1 where uid=$2")
|
||||
checkErr(err)
|
||||
|
||||
res, err := stmt.Exec("astaxieupdate", lastInsertId)
|
||||
checkErr(err)
|
||||
|
||||
affect, err := res.RowsAffected()
|
||||
checkErr(err)
|
||||
|
||||
fmt.Println(affect, "rows changed")
|
||||
|
||||
fmt.Println("# Querying")
|
||||
rows, err := db.Query("SELECT * FROM userinfo")
|
||||
checkErr(err)
|
||||
|
||||
for rows.Next() {
|
||||
var uid int
|
||||
var username string
|
||||
var department string
|
||||
var created time.Time
|
||||
err = rows.Scan(&uid, &username, &department, &created)
|
||||
checkErr(err)
|
||||
fmt.Println("uid | username | department | created ")
|
||||
fmt.Printf("%3v | %8v | %6v | %6v\n", uid, username, department, created)
|
||||
}
|
||||
|
||||
fmt.Println("# Deleting")
|
||||
stmt, err = db.Prepare("delete from userinfo where uid=$1")
|
||||
checkErr(err)
|
||||
|
||||
res, err = stmt.Exec(lastInsertId)
|
||||
checkErr(err)
|
||||
|
||||
affect, err = res.RowsAffected()
|
||||
checkErr(err)
|
||||
|
||||
fmt.Println(affect, "rows changed")
|
||||
}
|
||||
|
||||
func checkErr(err error) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
Обратите внимание, что PostgreSQL использует формат подстановки вида `$1,$2`, в отличие от MySQL, который использует `?`, а так же используется другой формат DSN при вызове `sql.Open`.
|
||||
Так же, имейте в виду, что Postgres не поддерживает `sql.Result.LastInsertId()`.
|
||||
Таким образом, вместо этого,
|
||||
|
||||
stmt, err := db.Prepare("INSERT INTO userinfo(username,departname,created) VALUES($1,$2,$3);")
|
||||
res, err := stmt.Exec("astaxie", "研发部门", "2012-12-09")
|
||||
fmt.Println(res.LastInsertId())
|
||||
|
||||
используйте `db.QueryRow()` и `.Scan()` для получения значения идентификатора последней вставки.
|
||||
|
||||
err = db.QueryRow("INSERT INTO TABLE_NAME values($1) returning uid;", VALUE1").Scan(&lastInsertId)
|
||||
fmt.Println(lastInsertId)
|
||||
|
||||
## Ссылки
|
||||
|
||||
- [Содержание](preface.md)
|
||||
- Предыдущий раздел: [SQLite](05.3.md)
|
||||
- Следующий раздел: [Разработка ORM на основе beedb](05.5.md)
|
||||
|
||||
|
||||
|
||||
52
zh/01.1.md
52
zh/01.1.md
@@ -1,16 +1,18 @@
|
||||
# 1.1 Go 安装
|
||||
# 1.1安装 Go
|
||||
|
||||
## Go的三种安装方式
|
||||
Go有多种安装方式,你可以选择自己喜欢的。这里我们介绍三种最常见的安装方式:
|
||||
|
||||
- Go源码安装:这是一种标准的软件安装方式。对于经常使用Unix类系统的用户,尤其对于开发者来说,从源码安装可以自己定制。
|
||||
- Go标准包安装:Go提供了方便的安装包,支持Windows、Linux、Mac等系统。这种方式适合快速安装,可根据自己的系统位数下载好相应的安装包,一路next就可以轻松安装了。**推荐这种方式**
|
||||
- 第三方工具安装:目前有很多方便的第三方软件包工具,例如Ubuntu的apt-get、Mac的homebrew等。这种安装方式适合那些熟悉相应系统的用户。
|
||||
- 第三方工具安装:目前有很多方便的第三方软件包工具,例如Ubuntu的apt-get和wget、Mac的homebrew等。这种安装方式适合那些熟悉相应系统的用户。
|
||||
|
||||
最后,如果你想在同一个系统中安装多个版本的Go,你可以参考第三方工具[GVM](https://github.com/moovweb/gvm),这是目前在这方面做得最好的工具,除非你知道怎么处理。
|
||||
|
||||
## Go源码安装
|
||||
在Go的源代码中,有些部分是用Plan 9 C和AT&T汇编写的,因此假如你要想从源码安装,就必须安装C的编译工具。
|
||||
Go 1.5彻底移除C代码,Runtime、Compiler、Linker均由Go编写,实现自举。只需要安装了上一个版本,即可从源码安装。
|
||||
|
||||
在Go 1.5前,Go的源代码中,有些部分是用Plan 9 C和AT&T汇编写的,因此假如你要想从源码安装,就必须安装C的编译工具。
|
||||
|
||||
在Mac系统中,只要你安装了Xcode,就已经包含了相应的编译工具。
|
||||
|
||||
@@ -76,7 +78,7 @@ Linux系统用户可通过在Terminal中执行命令`arch`(即`uname -m`)来查
|
||||
|
||||
### Mac 安装
|
||||
|
||||
访问[下载地址][downlink],32位系统下载go1.4.2.darwin-386-osx10.8.pkg,64位系统下载go1.4.2.darwin-amd64-osx10.8.pkg,双击下载文件,一路默认安装点击下一步,这个时候go已经安装到你的系统中,默认已经在PATH中增加了相应的`~/go/bin`,这个时候打开终端,输入`go`
|
||||
访问[下载地址][downlink],32位系统下载go1.4.2.darwin-386-osx10.8.pkg(最新版已无32位下载),64位系统下载go1.7.4.darwin-amd64.pkg,双击下载文件,一路默认安装点击下一步,这个时候go已经安装到你的系统中,默认已经在PATH中增加了相应的`~/go/bin`,这个时候打开终端,输入`go`
|
||||
|
||||
看到类似上面源码安装成功的图片说明已经安装成功
|
||||
|
||||
@@ -84,11 +86,11 @@ Linux系统用户可通过在Terminal中执行命令`arch`(即`uname -m`)来查
|
||||
|
||||
### Linux 安装
|
||||
|
||||
访问[下载地址][downlink],32位系统下载go1.4.2.linux-386.tar.gz,64位系统下载go1.4.2.linux-amd64.tar.gz,
|
||||
访问[下载地址][downlink],32位系统下载go1.7.4.linux-386.tar.gz,64位系统下载go1.7.4.linux-amd64.tar.gz,
|
||||
|
||||
假定你想要安装Go的目录为 `$GO_INSTALL_DIR`,后面替换为相应的目录路径。
|
||||
|
||||
解压缩`tar.gz`包到安装目录下:`tar zxvf go1.4.2.linux-amd64.tar.gz -C $GO_INSTALL_DIR`。
|
||||
解压缩`tar.gz`包到安装目录下:`tar zxvf go1.7.4.linux-amd64.tar.gz -C $GO_INSTALL_DIR`。
|
||||
|
||||
设置PATH,`export PATH=$PATH:$GO_INSTALL_DIR/go/bin`
|
||||
|
||||
@@ -115,35 +117,59 @@ Linux系统用户可通过在Terminal中执行命令`arch`(即`uname -m`)来查
|
||||
### GVM
|
||||
|
||||
gvm是第三方开发的Go多版本管理工具,类似ruby里面的rvm工具。使用起来相当的方便,安装gvm使用如下命令:
|
||||
```sh
|
||||
|
||||
bash < <(curl -s -S -L https://raw.githubusercontent.com/moovweb/gvm/master/binscripts/gvm-installer)
|
||||
|
||||
```
|
||||
安装完成后我们就可以安装go了:
|
||||
```sh
|
||||
|
||||
gvm install go1.4.2
|
||||
gvm use go1.4.2
|
||||
|
||||
gvm install go1.7.4
|
||||
gvm use go1.7.4
|
||||
```
|
||||
也可以使用下面的命令,省去每次调用gvm use的麻烦:
|
||||
gvm use go1.4.2 --default
|
||||
gvm use go1.7.4 --default
|
||||
|
||||
执行完上面的命令之后GOPATH、GOROOT等环境变量会自动设置好,这样就可以直接使用了。
|
||||
|
||||
### apt-get
|
||||
Ubuntu是目前使用最多的Linux桌面系统,使用`apt-get`命令来管理软件包,我们可以通过下面的命令来安装Go,为了以后方便,应该把 `git` `mercurial` 也安装上:
|
||||
```sh
|
||||
|
||||
sudo apt-get install python-software-properties
|
||||
sudo add-apt-repository ppa:gophers/go
|
||||
sudo apt-get update
|
||||
sudo apt-get install golang-stable git-core mercurial
|
||||
````
|
||||
###wget
|
||||
```sh
|
||||
|
||||
wget https://storage.googleapis.com/golang/go1.7.4.linux-amd64.tar.gz
|
||||
sudo tar -xzf go1.7.4.linux-amd64.tar.gz -C /usr/local
|
||||
export PATH=PATH:/usr/local/go/binexportGOROOT=HOME/go
|
||||
export PATH=PATH:GOROOT/bin
|
||||
export GOPATH=HOME/gowork
|
||||
```
|
||||
|
||||
### homebrew
|
||||
homebrew是Mac系统下面目前使用最多的管理软件的工具,目前已支持Go,可以通过命令直接安装Go,为了以后方便,应该把 `git` `mercurial` 也安装上:
|
||||
|
||||
1.安装homebrew
|
||||
|
||||
```sh
|
||||
|
||||
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
|
||||
```
|
||||
|
||||
2.安装go
|
||||
|
||||
```sh
|
||||
|
||||
brew update && brew upgrade
|
||||
brew install go
|
||||
brew install git
|
||||
brew install mercurial
|
||||
|
||||
brew install mercurial //可选安装
|
||||
```
|
||||
|
||||
## links
|
||||
* [目录](<preface.md>)
|
||||
|
||||
56
zh/01.4.md
56
zh/01.4.md
@@ -310,10 +310,66 @@ Gogland是JetBrains公司推出的Go语言集成开发环境,是Idea Go插件
|
||||
## Vim
|
||||
Vim是从vi发展出来的一个文本编辑器, 代码补全、编译及错误跳转等方便编程的功能特别丰富,在程序员中被广泛使用。
|
||||
|
||||
vim-go是vim上面的一款开源的go语言使用最为广泛开发环境的的插件
|
||||
|
||||
插件地址:[github.com/fatih/vim-go](https://github.com/fatih/vim-go)
|
||||
|
||||
vim的插件管理主要有[Pathogen](https://github.com/tpope/vim-pathogen)与[Vundle](https://github.com/VundleVim/Vundle.vim)
|
||||
,但是其作用的方面不同。
|
||||
pathogen是为了解决每一个插件安装后文件分散到多个目录不好管理而存在的。vundle是为了解决自动搜索及下载插件而存在的。
|
||||
这两个插件可同时使用。
|
||||
|
||||
1.安装Vundle
|
||||
|
||||
```sh
|
||||
mkdir ~/.vim/bundle
|
||||
git clone https://github.com/gmarik/Vundle.vim.git ~/.vim/bundle/Vundle.vim
|
||||
```
|
||||
|
||||
修改.vimrc,将Vundle的相关配置置在最开始处([详细参考Vundle的介绍文档](https://github.com/VundleVim/Vundle.vim))
|
||||
|
||||
```sh
|
||||
set nocompatible " be iMproved, required
|
||||
filetype off " required
|
||||
|
||||
" set the runtime path to include Vundle and initialize
|
||||
set rtp+=~/.vim/bundle/Vundle.vim
|
||||
call vundle#begin()
|
||||
|
||||
" let Vundle manage Vundle, required
|
||||
Plugin 'gmarik/Vundle.vim'
|
||||
|
||||
" All of your Plugins must be added before the following line
|
||||
call vundle#end() " required
|
||||
filetype plugin indent on " required
|
||||
|
||||
```
|
||||
2.安装Vim-go
|
||||
|
||||
修改~/.vimrc,在vundle#begin和vundle#end间增加一行:
|
||||
|
||||
```sh
|
||||
|
||||
Plugin 'fatih/vim-go'
|
||||
```
|
||||
|
||||
在Vim内执行: PluginInstall
|
||||
|
||||
3.安装YCM(Your Complete Me)进行自动补全
|
||||
在~/.vimrc中添加一行:
|
||||
```sh
|
||||
|
||||
Plugin 'Valloric/YouCompleteMe'
|
||||
```
|
||||
在Vim内执行: PluginInstall
|
||||
|
||||
|
||||

|
||||
|
||||
图1.9 VIM编辑器自动化提示Go界面
|
||||
|
||||
接着我们继续配置vim:
|
||||
|
||||
1. 配置vim高亮显示
|
||||
|
||||
cp -r $GOROOT/misc/vim/* ~/.vim/
|
||||
|
||||
@@ -49,7 +49,9 @@ goroutine是通过Go的runtime管理的一个线程管理器。goroutine通过`g
|
||||
|
||||
> runtime.Gosched()表示让CPU把时间片让给别人,下次某个时候继续恢复执行该goroutine。
|
||||
|
||||
>默认情况下,调度器仅使用单线程,也就是说只实现了并发。想要发挥多核处理器的并行,需要在我们的程序中显式调用 runtime.GOMAXPROCS(n) 告诉调度器同时使用多个线程。GOMAXPROCS 设置了同时运行逻辑代码的系统线程的最大数量,并返回之前的设置。如果n < 1,不会改变当前设置。以后Go的新版本中调度得到改进后,这将被移除。这里有一篇Rob介绍的关于并发和并行的文章:http://concur.rspace.googlecode.com/hg/talk/concur.html#landing-slide
|
||||
>默认情况下,在Go 1.5将标识并发系统线程个数的runtime.GOMAXPROCS的初始值由1改为了运行环境的CPU核数。
|
||||
|
||||
但在Go 1.5以前调度器仅使用单线程,也就是说只实现了并发。想要发挥多核处理器的并行,需要在我们的程序中显式调用 runtime.GOMAXPROCS(n) 告诉调度器同时使用多个线程。GOMAXPROCS 设置了同时运行逻辑代码的系统线程的最大数量,并返回之前的设置。如果n < 1,不会改变当前设置。
|
||||
|
||||
## channels
|
||||
goroutine运行在相同的地址空间,因此访问共享内存必须做好同步。那么goroutine之间如何进行数据的通信呢,Go提供了一个很好的通信机制channel。channel可以与Unix shell 中的双向管道做类比:可以通过它发送或者接收值。这些值只能是特定的类型:channel类型。定义一个channel时,也需要定义发送到channel的值的类型。注意,必须使用make 创建channel:
|
||||
|
||||
@@ -41,7 +41,7 @@ WebSocket的协议颇为简单,在第一次handshake通过以后,连接便
|
||||
## Go实现WebSocket
|
||||
Go语言标准包里面没有提供对WebSocket的支持,但是在由官方维护的go.net子包中有对这个的支持,你可以通过如下的命令获取该包:
|
||||
|
||||
go get code.google.com/p/go.net/websocket
|
||||
go get golang.org/x/net/websocket
|
||||
|
||||
WebSocket分为客户端和服务端,接下来我们将实现一个简单的例子:用户输入信息,客户端通过WebSocket将信息发送给服务器端,服务器端收到信息之后主动Push信息到客户端,然后客户端将输出其收到的信息,客户端的代码如下:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user