-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 46b5701
Showing
4 changed files
with
209 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
# Комментирование | ||
|
||
В Go предусмотрены блочные комментарии `/* */` в стиле `C` и строчные комментарии `//` в стиле `C++`. Строчные комментарии являются нормой; блочные комментарии появляются в основном как комментарии к пакетам, но они полезны внутри выражения или для отключения больших участков кода. | ||
|
||
Комментарии, появляющиеся перед декларациями верхнего уровня, без промежуточных строк, считаются документированием самой декларации. Эти «doc-комментарии» являются основной документацией для данного пакета или команды Go. Подробнее о комментариях см. в разделе [Комментарии к документам Go](https://go.dev/doc/comment). | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
# Форматирование | ||
|
||
Вопросы форматирования - самые спорные, но **наименее значимые**. Люди могут приспособиться к разным стилям форматирования, но лучше, если им не придется этого делать, и меньше времени будет уделено теме, если все будут придерживаться одного стиля. Проблема в том, как подойти к этой утопии без длинного предписывающего руководства по стилю. | ||
|
||
В Go мы используем необычный подход и позволяем машине позаботиться о большинстве вопросов форматирования. Программа `gofmt` (также доступная как `go fmt`, которая работает на уровне пакетов, а не исходных файлов) читает программу на Go и выдает исходный текст в стандартном стиле с отступами и вертикальным выравниванием, сохраняя и при необходимости переформатируя комментарии. Если вы хотите узнать, как поступить в новой ситуации с версткой, запустите `gofmt`; если ответ покажется вам неправильным, перестройте свою программу (или напишите об ошибке в `gofmt`), а не обходите ее. | ||
|
||
Например, не нужно тратить время на выстраивание комментариев к полям структуры. `Gofmt` сделает это за вас. Учитывая тэту структуру | ||
|
||
```go | ||
type T struct { | ||
name string // имя объекта | ||
value int // его значение | ||
} | ||
``` | ||
|
||
gofmt выстроит столбцы в ряд: | ||
|
||
```go | ||
type T struct { | ||
name string // имя объекта | ||
value int // его значение | ||
} | ||
``` | ||
|
||
Весь код Go в стандартных пакетах был отформатирован с помощью gofmt. | ||
|
||
Некоторые детали форматирования остались. **Очень коротко:** | ||
|
||
#### Отступы | ||
|
||
Мы используем табуляции для отступов, и `gofmt` выдает их по умолчанию. Используйте пробелы только в случае необходимости. | ||
|
||
#### Длина строки | ||
|
||
В Go нет ограничений на длину строки. Не бойтесь переполнить перфокарту. Если строка кажется слишком длинной, оберните ее и сделайте отступ с помощью дополнительной табуляции. | ||
|
||
Пример: | ||
|
||
```go | ||
package main | ||
|
||
import "fmt" | ||
|
||
func main() { | ||
// Длинная строка без переноса | ||
fmt.Println("This is an example of a very long line of code in Go that exceeds the usual length limit and should be wrapped according to best practices for readability and maintainability.") | ||
} | ||
|
||
``` | ||
|
||
```go | ||
package main | ||
|
||
import "fmt" | ||
|
||
func main() { | ||
// Длинная строка с множественными переносами | ||
fmt.Println( | ||
"This is an example of a very long line of code in Go that exceeds " + | ||
"the usual length limit and should be wrapped "+ | ||
"according to best practices for readability " + | ||
"and maintainability.", | ||
) | ||
} | ||
|
||
|
||
``` | ||
|
||
#### Круглые скобки | ||
|
||
В Go требуется меньше круглых скобок, чем в C и Java: управляющие структуры (if, for, switch) не содержат круглых скобок в своем синтаксисе. Кроме того, иерархия старшинства операторов короче и понятнее, так что | ||
|
||
x<<8 + y<<16 | ||
|
||
означает то, что подразумевает интервал, в отличие от других языков. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
module github.com/0x0FACED/effective-go-ru | ||
|
||
go 1.23.0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
# Имена | ||
|
||
Имена важны в Go, как и в любом другом языке. Они даже имеют семантический эффект: **видимость имени вне пакета определяется тем, является ли его первый символ верхним регистром**. Поэтому стоит уделить немного времени обсуждению соглашений об именовании в программах на Go. | ||
|
||
### Имена пакетов | ||
|
||
Когда пакет импортируется, его имя становится указателем доступа к его содержимому. После импорта пакета: | ||
|
||
```go | ||
import "strings" | ||
``` | ||
|
||
Мы можем использовать его содержимое: | ||
|
||
```go | ||
package main | ||
|
||
import "strings" | ||
|
||
func main() { | ||
str := "This is the new string!" | ||
words := strings.SplitN(str, " ", 3) | ||
// words = ["This","is","the new string!"] | ||
} | ||
bytes.Clone() | ||
|
||
``` | ||
|
||
Мы обращаемся к **экспортируемым** функциям/переменным/структурам через имя пакета. Это удобно, так как все, кто будут использовать этот пакет, будут использовать одинаковое обращение к его содержимому, что подразумевает, что имя пакета должно быть хорошим: коротким, лаконичным, вызывающим. По соглашению, пакетам присваиваются имена в нижнем регистре, состоящие из одного слова; подчеркивания или смешанные прописные буквы не нужны. Отдавайте предпочтение краткости, поскольку все, кто будет использовать ваш пакет, будут набирать это имя. И не беспокойтесь о коллизиях априори. Имя пакета - это только имя по умолчанию для импорта; оно не обязательно должно быть уникальным во всем исходном коде, и в редких случаях коллизии импортирующий пакет может выбрать другое имя для локального использования. | ||
|
||
Например: | ||
|
||
```go | ||
import ( | ||
"strings" | ||
"github.com/exampleuser/examplelibrary/strings" | ||
// Будет конфликт, так как оба пакета имеют одинаковые имена указателей доступа. | ||
// strings и strings | ||
) | ||
``` | ||
|
||
Сделаем так: | ||
|
||
```go | ||
import ( | ||
"strings" | ||
strings2 "github.com/exampleuser/examplelibrary/strings" | ||
// Конфликта не будет, как ты локально дали другое название пакету. | ||
) | ||
``` | ||
|
||
В любом случае, путаница возникает редко, поскольку имя файла в импорте определяет, какой именно пакет используется. | ||
|
||
Другое соглашение заключается в том, что имя пакета является базовым именем его исходного каталога; пакет в `src/encoding/base64` импортируется как `"encoding/base64"`, но имеет имя `base64`, а не `encoding_base64` и не `encodingBase64`. | ||
|
||
Импортер пакета будет использовать это имя для ссылки на его содержимое, поэтому экспортируемые имена в пакете могут использовать этот факт, чтобы избежать повторений. (Не используйте нотацию `import .`, которая может упростить тесты, которые должны выполняться вне проверяемого пакета, но в остальном ее следует избегать). Например, `buffered reader type` в пакете `bufio` называется `Reader`, а не `BufReader`, потому что пользователи видят его как `bufio.Reader`, **что является ясным и кратким именем**. Более того, поскольку к импортируемым сущностям всегда обращаются по имени их пакета, `bufio.Reader` не конфликтует с `io.Reader`. Аналогично, функция создания новых экземпляров `ring.Ring` - а это определение конструктора в Go - обычно называется `NewRing`, но поскольку `Ring` - единственный тип, экспортируемый пакетом, и поскольку пакет называется `ring`, она называется просто `New`, что клиенты пакета видят как `ring.New`. Используйте структуру пакета, чтобы помочь вам выбрать хорошие имена. | ||
|
||
Например, у нас есть пакет `internal/server`, где лежит структура сервера и есть функция, которая создает объект этого сервера. так как мы будем обращаться к содержимому пакета через `server`, во всех экспортируемых именах не стоит ипсользовать `server`. | ||
|
||
```go | ||
package server | ||
|
||
type server struct { | ||
// ... | ||
} | ||
|
||
func New(...) *server { | ||
return &server{ | ||
... | ||
} | ||
} | ||
|
||
// ... | ||
``` | ||
|
||
```go | ||
package client | ||
|
||
import ".../internal/server" | ||
|
||
func main() { | ||
s := server.New(...) | ||
// ... | ||
} | ||
``` | ||
|
||
#### *Что здесь у нас?* | ||
|
||
У нас есть пакет со структурой сервера и функция, которая инициализирует объект сервера и возвращает указатель на него. **Структура НЕэкспортируемая, а функция экспортируемая**. В другом месте мы импортируем пакет сервера и хотим создать сервер + получить на него указатель. Мы вызываем `server.New()`, **что уже явно говорит нам, что создается именно сервер**. Не надо делать никаких `NewServer()` и других. | ||
|
||
Конечно, если будет еще другая структура в этом пакете и у нее будет функция `New()`, то тогда придется выбрать другое название. **А вообще надо просто создать отдельный пакет для этой структуры и всего, что с ней связано.** | ||
|
||
### Геттеры (Getters) | ||
|
||
Go не предоставляет автоматической поддержки геттеров и сеттеров. Нет ничего плохого в том, чтобы предоставлять геттеры и сеттеры самостоятельно, и часто это бывает уместно, **но нет ни идиоматизма, ни необходимости добавлять Get в имя геттера**. Если у вас есть поле с именем `owner` (**нижний регистр, неэкспортированное**), **метод геттера должен называться `Owner`** (**верхний регистр, экспортированное**), **а не** `GetOwner`. Использование имен в верхнем регистре при экспорте дает крючок для различения поля и метода. Функция сеттера, если она необходима, скорее всего, будет называться `SetOwner`. Оба имени хорошо читаются на практике: | ||
```go | ||
owner := obj.Owner() | ||
if owner != user { | ||
obj.SetOwner(user) | ||
} | ||
``` | ||
|
||
### Имена интерфейсов | ||
|
||
По соглашению, интерфейсы с одним методом называются по имени метода с добавлением суффикса `-er` или аналогичного изменения для образования существительного-агента: `Reader`, `Writer`, `Formatter`, `CloseNotifier` и т.д. | ||
|
||
Существует множество таких имен, и важно уважать их и функции, которые они описывают. `Read`, `Write`, `Close`, `Flush`, `String` и так далее **имеют канонические сигнатуры и значения**. Чтобы избежать путаницы, не давайте вашему методу одно из этих имен, если у него нет такой же сигнатуры и значения. Напротив, если ваш тип реализует метод с тем же значением, что и метод в известном типе, дайте ему такое же имя и сигнатуру; назовите ваш метод преобразования строки `String`, а не `ToString`. | ||
|
||
#### *Что за канонические сигнатуры и значения?* | ||
|
||
В Go существуют **стандартные (канонические) имена и сигнатуры методов для интерфейсов**. Например, методы `Read`, `Write`, `Close`, и `String` имеют общепринятые значения и способы использования в языке. | ||
|
||
Вот что это означает: | ||
|
||
1. *Стандартные имена и сигнатуры*. Методы с определенными именами имеют ожидаемые сигнатуры (определение параметров и возвращаемых значений). Например, метод `Read` в стандартной библиотеке имеет сигнатуру `Read(p []byte) (n int, err error)`, **что означает, что он читает данные в срез байтов и возвращает количество прочитанных байтов и ошибку**. | ||
|
||
2. *Согласованность и предсказуемость*. Когда вы используете стандартные имена и сигнатуры, это делает ваш код предсказуемым и совместимым с другими пакетами и библиотеками, которые следуют тем же стандартам. Это помогает другим разработчикам быстрее понять ваш код и использовать его без необходимости разбираться в нестандартных соглашениях. | ||
|
||
Не используйте стандартные имена без соответствия сигнатурам: не называйте ваш метод, например, `Read`, **если он не реализует ту же функциональность или сигнатуру, что и стандартный метод `Read`**. Если вы используете стандартное имя, оно должно точно соответствовать общепринятым стандартам, чтобы избежать путаницы. Например, метод `Read` должен принимать **срез байтов** и **возвращать количество прочитанных байтов и ошибку**, как это предусмотрено стандартом. | ||
|
||
Соблюдайте соглашения для методов с известными именами: если ваш тип реализует метод, аналогичный методу в известных типах (например, `String` для метода, возвращающего строку), используйте те же имена и сигнатуры. Это делает ваш код согласованным с другими библиотеками и типами, что упрощает его использование и понимание. Например, метод, **который возвращает строковое представление вашего типа, должен называться `String`, а не `ToString`.** | ||
|
||
### MixedCaps | ||
|
||
Наконец, в Go принято использовать **MixedCaps** или **mixedCaps**, **а не подчеркивание для записи многословных имен**. |