Оператори та вирази¶
Повний список¶
Арифметичні¶
Порівняння¶
Порівняння завжди повертає bool.
Логічні (булеві)¶
Обчислюються ліниво, як у Python. Операнди мають бути bool — неявного перетворення на основі істинності немає.
Побітові¶
& // AND
| // OR
^ // XOR (також унарний NOT)
&^ // AND NOT — специфічний для Go, скидає біти
<< // зсув ліворуч
>> // зсув праворуч
Складені присвоєння¶
Отримання адреси / розіменування¶
Переповнення цілих чисел загортається¶
Переповнення цілих чисел використовує арифметику доповнення до двох — без невизначеної поведінки, без паніки.
var x uint8 = 255
x++
fmt.Println(x) // 0 — загортається
var y int8 = 127
y++
fmt.Println(y) // -128
Якщо потрібно виявляти переповнення, використовуйте Add64/Mul64 тощо з пакету math/bits, або перевіряйте діапазони вручну перед арифметикою.
З досвіду Python:
intу Python зростає без обмежень. Цілі числа Go мають фіксовану ширину й загортаються. Для довільної точності зверніться доmath/big.Int.
Ділення цілих чисел скорочує до нуля¶
fmt.Println(7 / 2) // 3
fmt.Println(-7 / 2) // -3 (до нуля, а не до -нескінченності)
fmt.Println(7 % 2) // 1
fmt.Println(-7 % 2) // -1
З досвіду Python:
/у Python 3 завжди повертає float (7 / 2 == 3.5); для цілого ділення з округленням вниз використовується//(-7 // 2 == -4). Оператор/у Go є цілочисельним якщо обидва операнди є цілими, і скорочує до нуля — як у C.
Немає оператора піднесення до степеня¶
У Go немає **. Використовуйте math.Pow:
import "math"
fmt.Println(math.Pow(2, 10)) // 1024 — float64
// Для степенів двійки використовуйте зсув:
fmt.Println(1 << 10) // 1024
Немає тернарного оператора¶
Спільнота вирішила, що читабельність важливіша за стислість. Використовуйте if/else або допоміжну функцію:
// Python: status := "even" if n%2 == 0 else "odd"
var status string
if n%2 == 0 {
status = "even"
} else {
status = "odd"
}
Якщо дуже хочеться однорядниковий варіант, напишіть крихітну узагальнену допоміжну функцію. Частина [T any] — це синтаксис узагальненого програмування (generics) у Go: параметр типу, що дозволяє одній функції працювати зі значеннями будь-якого типу. Узагальнення розглядатимуться окремо; наведений нижче приклад — лише для того, щоб показати, як такий помічник виглядає.
func If[T any](cond bool, a, b T) T {
if cond { return a }
return b
}
status := If(n%2 == 0, "even", "odd")
Але більшість коду на Go просто використовує чотирирядковий if/else. Не воюйте з мовою.
++ та -- — це оператори-вирази, а не вирази¶
i++ // ok — оператор
j-- // ok — оператор
x := i++ // compile error: i++ is not an expression
if i++ > 10 {} // compile error
i++ ніколи не можна використовувати всередині виразу. І префіксних форм ++i / --i не існує.
&^ — AND NOT (скидання бітів)¶
Унікальний для Go. a &^ b еквівалентно a & (^b) — скидає в a ті біти, що встановлені в b.
const (
Readable = 1 << 0 // 0b001
Writable = 1 << 1 // 0b010
Executable = 1 << 2 // 0b100
)
perms := Readable | Writable | Executable // 0b111
perms = perms &^ Writable // 0b101 — біт запису скинуто
Конкатенація рядків¶
s := "hello" + ", " + "world" // працює, але кожен + виконує виділення пам'яті
s := fmt.Sprintf("%s, %s", "hello", "world")
import "strings"
s := strings.Join([]string{"hello", "world"}, ", ")
// Для багатьох конкатенацій у циклі використовуйте Builder:
var b strings.Builder
for _, w := range words {
b.WriteString(w)
b.WriteString(" ")
}
result := b.String()
Конкатенація з + у щільному циклі має складність O(n²) — кожен + копіює весь попередній префікс. strings.Builder — O(n).
З досвіду Python: ≈ обговорення
+проти''.join(...). Та сама порада: для циклів використовуйте builder.
Пріоритет операторів¶
П'ять рівнів від найнижчого до найвищого:
Унарні оператори (!, -, ^, *, &, <-) мають вищий пріоритет, ніж будь-який бінарний оператор.
Якщо сумніваєтесь — ставте дужки. Рецензенти коду надають перевагу явним дужкам перед покладанням на таблицю пріоритетів.
Пастки при порівнянні¶
- Числа з рухомою комою порівнюються точно, але з нюансом.
0.1 + 0.2 == 0.3, записаний як голі літерали, єtrue— Go обчислює нетипізовані константні вирази під час компіляції з довільною точністю, тому округлення IEEE 754 не відбувається. Як тільки ті самі числа живуть у зміннихfloat64, округлення вступає в силу і відповідь змінюється:
fmt.Println(0.1 + 0.2 == 0.3) // true (untyped constants)
var a, b, c float64 = 0.1, 0.2, 0.3
fmt.Println(a + b == c) // false (float64 variables)
Використовуйте порівняння з епсилоном щоразу, коли порівнюєте змінні float32/float64.
- Рядки порівнюються байт за байтом (лексикографічно). "a" < "b" є true.
- Зрізи, мапи, функції не можна порівнювати за допомогою ==/!= — лише з nil. Використовуйте reflect.DeepEqual або написану вручну перевірку.
Джерела¶
- Оператори — go.dev/ref/spec#Operators
- Арифметичні оператори — go.dev/ref/spec#Arithmetic_operators
- Оператори порівняння — go.dev/ref/spec#Comparison_operators
- Переповнення цілих (доповнення до двох) — go.dev/ref/spec#Integer_overflow
strings.Builder— pkg.go.dev/strings#Buildermath/bits— pkg.go.dev/math/bits