Skip to content

Commit

Permalink
init
Browse files Browse the repository at this point in the history
  • Loading branch information
0x0FACED committed Aug 29, 2024
0 parents commit 46b5701
Show file tree
Hide file tree
Showing 4 changed files with 209 additions and 0 deletions.
6 changes: 6 additions & 0 deletions commentary/commentary.md
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).

75 changes: 75 additions & 0 deletions formatting/formatting.md
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

означает то, что подразумевает интервал, в отличие от других языков.
3 changes: 3 additions & 0 deletions go.mod
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
125 changes: 125 additions & 0 deletions names/names.md
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**, **а не подчеркивание для записи многословных имен**.

0 comments on commit 46b5701

Please sign in to comment.