Olá pessoal!
Meu nome é Lex e eu sou o apresentador do canal
IT Co. no youtube E eu sou um associado de 6 anos de idade. Recentemente, eu queria ir além da minha tecnologia principal (C # /. NET) e entender a essência do
paradoxo do Blob . Eu decidi firmemente que me tentaria em outro idioma, e a escolha por acaso recaiu em Go.
Para obter conhecimento de maneira estruturada, inscrevi-me em um curso no mail.ru, que você pode encontrar aqui:
https://www.coursera.org/learn/golang-webservices-1 . Na verdade, o que aprendi na primeira semana de treinamento neste curso será discutido mais adiante. Vamos lá!
Vou começar com uma pequena introdução ao idioma.
O Go já foi desenvolvido na época dos processadores com vários núcleos (2007-2009), então tudo é muito bom aqui com o trabalho paralelo entre os núcleos. Além disso, o idioma funciona muito bem com solicitações competitivas (simultâneas). Em geral, uma descoberta para todos os tipos de serviços da web e sistemas da web carregados. Apenas o que é necessário para mim, porque tenho desenvolvido API da Web (Web API) nos últimos anos.
Iniciar
Para trabalhar com o idioma, basta instalar
o pacote de
software “Go tools” de 118mb de tamanho e você pode começar a codificar.
Os scripts têm a
extensão * .go e são executados pelo comando
go na linha de comando (sou adepto da linha de comando do Windows). O comando
gofmt coloca todos os espaços de indentação e produz doces a partir do arquivo (embelezador de código fora da caixa). Como na minha sub-rede favorita, no Go, a execução do programa começa com o método
principal .
Um recurso interessante chamou minha atenção imediatamente -
você não pode colocar um ponto e vírgula no final da linha :)
Go é outra linguagem (juntamente com python, c #, php, js, ts e outras), conveniente para trabalhar no VSCode.
O próprio
VSCode oferece absolutamente todos os pacotes e dependências necessários à medida que o código é escrito. Ao salvar o IDE, execute o gofmt para você e torne o código ainda mais bonito.
Variáveis
Tudo é familiar aqui: existem muitos tipos diferentes, existem valores padrão. De incomum para um doador, a palavra-chave
var significa que a variável irá além, e o tipo da variável pode ser declarado ou ainda não após o nome da variável. Invulgarmente.
Em geral, existem muitas maneiras de declarar e inicializar variáveis. Você pode até atribuir valores a várias variáveis, separadas por vírgulas, na mesma linha. E, a propósito, não há modelos implícitos.
O And Go também possui um operador "
: = ", que permite declarar e inicializar imediatamente novas (e quase sempre apenas novas) variáveis. Olá Pascal;)
Visualização de registro:
perem1, perem2 := 2, 3
cria duas novas variáveis ou cria uma delas e a segunda simplesmente atribui um novo valor. Isso significa que, se as duas variáveis já existirem, isso não funcionará e você receberá um erro, elas dizem usar a atribuição usual. Operador
: = deve criar algo :)
Há também uma falsificação engraçada - o caractere sublinhado "_". Significa a ausência de uma variável, algo parecido com isso :)
(nos detalhes, como se viu, também é: https://docs.microsoft.com/en-us/dotnet/csharp/discards )
Momento divertido com strings: a contagem padrão do comprimento de uma string
len (str) conta
bytes e, em UTF8, um caractere pode pesar mais. Para contar os caracteres chamados
runas , você precisa usar o
método utf8.RuneCountInString (str) do pacote “utf8”. E, no entanto - não alteramos as linhas (como no seu favorito).
Constantes
As constantes podem ser declaradas em lotes. As constantes têm um tipo interessante, ou algo assim -
iota . Vou chamá-lo de Jopta. O Jopta permite fazer algumas constantes com base em outras, como um iterador para constantes (algo que lembra o
rendimento de uma sub-rede).
E, no entanto, as constantes não podem ser digitadas. Para essas constantes, o compilador decide que tipo elas são no momento certo. Convenientemente, o que já está lá.

Ponteiros
Sim sim Há dicas aqui, mas não parece muito assustador. Eles dizem que não estão numerados e não é possível obter o próximo ponteiro adicionando um par ao ponteiro. Aqui é como uma variável que armazena um link para outra variável (até agora, de alguma forma, eu a entendi).
O ponteiro é especificado por meio de um ampersante ou do
novo método (
tipo) (para obter um ponteiro para um tipo, não para uma variável):

Se você acessar o ponteiro diretamente, o link será alterado. E se através do operador "
* ", o valor da variável que está por trás do ponteiro (para o qual aponta) é alterado. Em geral, ainda não está muito claro, mas, dizem, será mais claro.
Matrizes
O tamanho da matriz faz parte do tipo de dados! Portanto, matrizes de diferentes dimensões são de fato tipos diferentes de dados e não são compatíveis.

Mas o que para matrizes é necessário se elas são tão secas e imutáveis (elas não podem ser alteradas em tempo de execução). Portanto, criamos essas
fatias (fatias) com base em matrizes.

Uma fatia tem um
comprimento e
capacidade . Isso me lembrou um pouco do tipo de dados
nvarchar do SQL. I.e. você pode alocar espaço de memória (alocação de memória) para suas matrizes, mas colocar matrizes de qualquer tamanho (capacidade). Em suma, a coisa é interessante, você precisa se acostumar. Para uso conveniente de fatias, existem todos os tipos de métodos, como
make (), append (), len (), cap () e, com certeza, outros. De uma fatia (fatia), você pode obter um subfatia (fatia de fatia). E, a propósito,
append () aumenta a capacidade da fatia.
Se a sub-fatia for igual à fatia, na verdade eles se referirão a um pedaço de memória. E, portanto, uma alteração no valor em uma fatia levará a uma alteração na segunda fatia. Mas se um deles for expandido através de
append () , um novo pedaço de memória será alocado.
Em suma, tudo é sério com a memória :)
Mapa, tabela de hash, matriz associativa
Tudo isso é o mesmo. Vá a tabelas de hash para pesquisas rápidas de teclas. Inicializado da seguinte maneira:

Você pode criar subtabelas (Mapa), etc. (qualquer nível de aninhamento, mais ou menos). Há um valor padrão que retorna em chaves inexistentes. É retirado do tipo de chave (para bool, depois false). Para saber se uma variável retornou por padrão, ou seja, que a chave está faltando - use a chave para a existência da chave (coisa interessante):

Estruturas de controle
Existe um if-else. Um pouco mais interessante que nos sharps, porque você pode usar as condições de inicialização para if.
Há um caso de swith. As quebras não precisam ser definidas, e isso é uma diferença. Inversão da lógica) Se você deseja que a seguinte condição também seja verificada, é necessário escrever uma
explicação .
Existe para. E tudo com ciclos.

Com uma fatia iterativa mais divertida (operador de intervalo):

E mesmo pelo mapa de hash, você pode iterar (embora a sequência geralmente seja diferente, porque o mapa não é direcional):

Para o tipo
string, o intervalo percorre runas, não bytes.
Funções
Declarado através da palavra-chave
func . Além disso, a inversão da lógica é novamente comparada à sub-rede - os parâmetros de entrada são indicados primeiro e depois o tipo do valor de retorno (você pode nomeá-lo imediatamente).
As funções podem retornar
vários resultados , assim como as
tuplas em uma sub - rede .
Os valores nomeados que a função retorna são inicializados por padrão com valores padrão para o tipo.
E, no entanto, você pode criar
funções variáveis que tenham um número ilimitado de parâmetros de entrada do mesmo tipo (como param na sub-rede). E então um tipo específico de fatia chega à entrada da função.
As funções podem ter nomes ou podem não ter um nome -
anônimo . E eles podem ser chamados, podem ser atribuídos a variáveis. Parece JavaScript. Você pode até criar tipos com base em funções! Funções podem ser passadas como parâmetros (ler delegados da sub-rede)
Há até um
fechamento (uuuh, assustador)! Você pode acessar variáveis da função externa (leia-se, na função pai). Estas são as tortas.
Você pode declarar a
execução adiada de funções . Através da palavra-chave
adiar, isso é feito. I.e. as funções diferidas são executadas no final da função em que são declaradas, na ordem inversa da declaração dessas funções diferidas. Aqui. Além disso, a inicialização dos argumentos das funções adiadas ocorre quando o bloco adiado é
declarado . Isso é importante, especialmente se outra função for tomada como argumento - ela será executada muito mais cedo do que o esperado!
PÂNICO! Go tem essa função -
panic () . É como
jogar , mas pior. Esta função interrompe a execução do programa. Mas esse mesmo pânico pode ser processado por adiamento, pois é executado de qualquer forma no final da função, mesmo após um pânico. E, no entanto - em pânico, isso não é uma tentativa de captura. Isso é pior!
Estruturas (objetos próximos)
Diz-se que Go não é exatamente uma linguagem de paradigma OOP. Mas existem coisas como
estruturas . Se você é do mundo das sub-redes, sabe que também temos estruturas - esses são mapeamentos de objetos que podem ser armazenados na pilha (tipos de valor). No Go, a estrutura é a única maneira de fazer algo como um objeto. Ainda não posso falar sobre os tipos de valor, mas a natureza da interação com as estruturas é muito semelhante à detalhada.
Cada estrutura é essencialmente um tipo separado e pode ter um conjunto de propriedades de tipos específicos (incluindo o tipo de outra estrutura ou tipo de função):

Além disso, um certo mecanismo de herança direta de objetos da sub-rede é visto aqui. Na figura acima, a estrutura da
conta está aninhada com a estrutura da
pessoa . Isso significa que todos os campos de
Pessoa estarão disponíveis em uma variável do tipo
Conta . Se os nomes das propriedades coincidirem (no exemplo acima
Id, Nome ), não haverá conflito, mas o valor de um campo superior será utilizado:

Métodos
Sim, não há apenas a semelhança de objetos, mas também métodos. Quase OOP :) Um método é uma
função vinculada a um tipo específico (por exemplo, a uma estrutura). Um método difere de uma função em uma declaração: após a palavra-chave
func , entre colchetes você precisa especificar o tipo ao qual esse método pertence e a função de passar uma variável desse tipo. Qual é o papel? Se você colocar um asterisco na frente do tipo, a variável será passada por referência (lembre-se dos ponteiros, também estava lá) e, como resultado, dentro do método, trabalharemos com um tipo específico, e não uma cópia dele.

Da figura acima, podemos concluir que
UpdateName não faz sentido, porque altera a cópia da estrutura, e não o original. E esta cópia não retorna. Enquanto
SetName mudará a estrutura original (graças ao asterisco e passando por referência).
Os métodos nas estruturas
são herdados (e de acordo com as regras de herança de propriedade), ou seja, a estrutura pai tem acesso a todos os métodos aninhados em suas estruturas.
Outros tipos podem ter métodos, não apenas estruturas. Isso pode parecer métodos de extensão da sub-rede, mas não. Os métodos no Go podem ser criados apenas para tipos locais, ou seja, tipos que são declarados neste pacote (sobre pacotes um pouco mais).
Pacotes, escopo, namespaces
Só agora eu percebi que o Go não tem NEIMSPACE! Geralmente! Percebi isso quando, após a compilação, ocorreu um erro, eles disseram que em uma pasta eu tenho dois arquivos com o método principal. Acontece que, na fase de compilação, tudo do papai parece estar colado em uma única tela! Uma pasta de fato é um espaço para nome. Isso é tão mágico, camaradas :)
A propósito, aqui está o que a doca diz:

Nota para si mesmo:
fumar uma docaE aqui está outro pequeno artigo no tópico:
https://www.callicoder.com/golang-packages/#the-main-package-and-main-functionEntão, eles dizem, a estrutura básica de pastas se parece com:

bin - binários compilados
pkg - arquivos de objetos temporários
src - fontes
Pacotes referem-se ao nome da pasta na qual os arquivos do pacote estão localizados. Sim, um pacote pode ter muitos arquivos importados um para o outro.
Outra regra interessante: funções, propriedades, variáveis e constantes começando com uma letra maiúscula
podem ser usadas fora do pacote , ou seja,
ser importado . Tudo com uma letra minúscula é usado apenas dentro da embalagem. Um análogo
dos modificadores de acesso da sub-rede.
A palavra-chave
import funciona dentro de um arquivo, não em um pacote.
Interfaces
As interfaces aqui são interessantes. Não precisamos herdar interfaces de nossas estruturas. É suficiente que a estrutura, que vem como um parâmetro de entrada para um determinado método que usa um tipo de interface, implemente os métodos dessa interface. E não há problemas. I.e. métodos de interface não precisam ser necessariamente implementados pela estrutura. Mas a estrutura deve conter a implementação de todos os métodos necessários em uma função específica, em que o tipo de interface é usado como um parâmetro de entrada.
Acontece que, ao contrário do Go, a interface é uma característica da função em que é usada, e não uma característica de uma estrutura específica (objeto).
Se for ainda mais simples, a lógica é a seguinte: você não precisa ser capaz de grasnar para ser um pato. Se você sabe grasnar, provavelmente você é um pato :)
E, a propósito, se sua estrutura implementa todos os métodos de alguma interface, essa estrutura
pode ser atribuída como os valores da variável da interface implementada . Algo assim :)
Tipo caixa do interruptor
O Go possui uma caixa de comutação especial que pode funcionar dependendo do tipo de entrada. Coisa legal:

A propósito, é assim que você pode
converter um tipo para outro (por exemplo, um tipo de interface em um tipo de estrutura para obter campos de estrutura que não são acessíveis a partir da interface):
ok - sinal booleano de que a conversão foi bem-sucedida. Já encontramos os sinais :)
A interface vazia é a besta no mundo de Go. Pode levar qualquer tipo. Algo como
dinâmico ou
Objeto . Por exemplo, é usado em
fmt.Println () e funções semelhantes que usam uma interface vazia na implementação.
As interfaces podem ser construídas umas nas outras e, assim, criar a composição de interfaces (o que é dito na letra
I (segregação de interfaces) dos princípios do
SOLID )
Testes
No Go, todas as funções de teste começam com a palavra
Teste e a entrada do teste do módulo de
teste é aceita. Os arquivos são nomeados pelo nome do arquivo de teste + palavra _test (
main_test.go - testa o arquivo
main.go ). Além disso, os testes e os arquivos de teste estão em um pacote!

Epílogo
Isso é tudo! Agradecemos sua atenção e estou pronto para discutir questões nos comentários. Encontro você nas seguintes notas das minhas aulas!
PS Você pode olhar para todo o meu código nesta semana de treinamento no
github